-
Notifications
You must be signed in to change notification settings - Fork 8
/
smtp.go
143 lines (121 loc) · 3.67 KB
/
smtp.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
package main
import (
"errors"
"net"
"net/smtp"
"time"
)
var (
SMTPDisabledError = errors.New("SMTP disabled in the configuration")
)
// ConnectSMTP uses the global Conf to connect to the SMTP server and,
// unless disabled in the configuration, authenticates with STARTTLS
// if possible. Unless an error is returned, t is the caller's
// responsibility to close the client. If the Conf.SMTP is nil,
// STMPDisabledError will be returned.
func ConnectSMTP() (c *smtp.Client, err error) {
// If the SMTP portion of the config isn't specified, we don't
// know what to connect to.
if Conf.SMTP == nil {
return nil, SMTPDisabledError
}
// Connect to the server.
c, err = smtp.Dial(Conf.SMTP.ServerAddress)
if err != nil {
return
}
// If NoAuthenticate is true, then skip the authentication step.
if !Conf.SMTP.NoAuthenticate {
// Upgrade to STARTTLS if supported.
if ok, _ := c.Extension("STARTTLS"); ok {
if err = c.StartTLS(nil); err != nil {
c.Quit()
return
}
}
// Authenticate using the password via plain auth.
host, _, err := net.SplitHostPort(Conf.SMTP.ServerAddress)
if err != nil {
return nil, err
}
if err = c.Auth(smtp.PlainAuth("", Conf.SMTP.Username,
Conf.SMTP.Password, host)); err != nil {
// If the authentication fails, close the client and exit.
c.Quit()
return nil, err
}
}
// If all is successful, return.
return
}
// PrepareEmail connects to the SMTP server configured in the Conf and
// prepares an email with the given fields. The caller should call the
// Data() method on the *smtp.Client and write the body directly to
// the io.WriteCloser it returns. To send, invoke the Client's Quit()
// method.
func PrepareEmail(from, to string) (c *smtp.Client, err error) {
// Connect to the SMTP server and authenticate.
c, err = ConnectSMTP()
if err != nil {
return
}
// TODO(DuoNoxSol): Check for correct-looking email addresses?
// Set the sender and recipient.
if err = c.Mail(from); err != nil {
c.Quit()
return nil, err
}
if err = c.Rcpt(to); err != nil {
c.Quit()
return nil, err
}
// Now, return the Client.
return c, nil
}
// SendVerificationEmail uses the fields in Conf.SMTP to send a
// templated email (verification.txt) to the email address specified
// by the given node. If the email could not be sent, it returns an
// error.
func SendVerificationEmail(id int64, n *Node) (err error) {
// Prepare an Email type.
e := &Email{
To: n.OwnerEmail,
From: Conf.SMTP.EmailAddress,
Subject: Conf.Name + " Node Registration",
}
e.Data = make(map[string]interface{}, 3)
e.Data["Link"] = Conf.Web.Hostname + Conf.Web.Prefix
e.Data["VerificationID"] = id
e.Data["FromNode"] = Conf.Verify.FromNode
return e.Send("verification.txt")
}
// Email simplifies the process of crafting and sending emails via
// SMTP. It makes use of the global *template.Template t.
type Email struct {
// To, From, and Subject are standard pieces of an Email template.
To, From, Subject string
// Data contains any additional, which should be referenced by
// name in the template, e.g. '{{.Data.FieldName}}'.
Data map[string]interface{}
// Header contains data which is generated at Send() time. It does
// not need to need to be filled out.
Header struct {
Date string
}
}
func (e *Email) Send(templateName string) (err error) {
c, err := PrepareEmail(Conf.SMTP.EmailAddress, e.To)
if err != nil {
return
}
defer c.Quit()
e.Header.Date = time.Now().Format(time.RFC1123Z)
// Tell the server we're about to send it the data.
w, err := c.Data()
if err != nil {
return
}
// Execute the template verification.txt and write directly to the
// connection.
return t.ExecuteTemplate(w, templateName, e)
}