-
Notifications
You must be signed in to change notification settings - Fork 35
/
smtp.go
195 lines (168 loc) · 4.64 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package gold
import (
"crypto/tls"
"fmt"
"net/mail"
"net/smtp"
"strconv"
"strings"
)
type EmailStruct struct {
To string
ToName string
From string
FromName string
Subject string
Body string
}
// EmailConfig holds configuration values for remote SMTP servers
type EmailConfig struct {
// Name of the remote SMTP server account, i.e. Server admin
Name string
// Addr is the remote SMTP server email address, i.e. [email protected]
Addr string
// User is the remote SMTP server username, i.e. admin
User string
// Pass is the remote SMTP server password
Pass string
// Host is the remote SMTP server IP address or domain
Host string
// Port is the remote SMTP server port number
Port int
// ForceSSL forces SSL/TLS connection instead of StartTLS
ForceSSL bool
// Insecure allows connections to insecure remote SMTP servers (self-signed certs)
Insecure bool
}
func NewEmailStruct() *EmailStruct {
return &EmailStruct{}
}
func (s *Server) sendWelcomeMail(params map[string]string) {
email := NewEmailStruct()
email.To = params["{{.To}}"]
email.ToName = params["{{.Name}}"]
email.From = params["{{.From}}"]
email.FromName = "Notifications Service"
email.Subject = "Welcome to " + params["{{.Host}}"] + "!"
email.Body = parseMailTemplate("welcomeMail", params)
s.sendMail(email)
}
func (s *Server) sendRecoveryMail(params map[string]string) {
email := NewEmailStruct()
email.To = params["{{.To}}"]
email.ToName = params["{{.Name}}"]
email.From = params["{{.From}}"]
email.FromName = "Account Recovery"
email.Subject = "Recovery instructions for your account on " + params["{{.Host}}"]
email.Body = parseMailTemplate("accountRecovery", params)
s.sendMail(email)
}
// should be run in a go routine
func (s *Server) sendMail(email *EmailStruct) {
if &s.Config.SMTPConfig == nil {
s.debug.Println("Missing smtp server configuration")
}
smtpCfg := &s.Config.SMTPConfig
auth := smtp.PlainAuth("",
smtpCfg.User,
smtpCfg.Pass,
smtpCfg.Host,
)
// Setup headers
src := mail.Address{Name: email.FromName, Address: email.From}
dst := mail.Address{Name: email.ToName, Address: email.To}
headers := make(map[string]string)
headers["From"] = src.String()
headers["To"] = dst.String()
headers["Subject"] = email.Subject
headers["MIME-Version"] = "1.0"
headers["Content-Type"] = "text/html; charset=\"utf-8\""
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + email.Body
if len(smtpCfg.Host) > 0 && smtpCfg.Port > 0 && auth != nil {
smtpServer := smtpCfg.Host + ":" + strconv.Itoa(smtpCfg.Port)
var err error
// force upgrade to full SSL/TLS connection
if smtpCfg.ForceSSL {
err = s.sendSecureMail(src, dst, []byte(message), smtpCfg)
} else {
err = smtp.SendMail(smtpServer, auth, smtpCfg.Addr, []string{email.To}, []byte(message))
}
if err != nil {
s.debug.Println("Error sending recovery email to " + email.To + ": " + err.Error())
} else {
s.debug.Println("Successfully sent recovery email to " + email.To)
}
} else {
s.debug.Println("Missing smtp server and/or port")
}
}
func (s *Server) sendSecureMail(from mail.Address, to mail.Address, msg []byte, cfg *EmailConfig) (err error) {
// Connect to the SMTP Server
serverName := cfg.Host + ":" + strconv.Itoa(cfg.Port)
auth := smtp.PlainAuth("", cfg.User, cfg.Pass, cfg.Host)
// TLS config
tlsconfig := &tls.Config{
InsecureSkipVerify: s.Config.SMTPConfig.Insecure,
ServerName: cfg.Host,
}
// Here is the key, you need to call tls.Dial instead of smtp.Dial
// for smtp servers running on 465 that require an ssl connection
// from the very beginning (no starttls)
conn, err := tls.Dial("tcp", serverName, tlsconfig)
if err != nil {
s.debug.Println(err.Error())
return
}
defer conn.Close()
c, err := smtp.NewClient(conn, cfg.Host)
if err != nil {
s.debug.Println(err.Error())
return
}
// Auth
if err = c.Auth(auth); err != nil {
s.debug.Println(err.Error())
return
}
// To && From
if err = c.Mail(from.Address); err != nil {
s.debug.Println(err.Error())
return
}
if err = c.Rcpt(to.Address); err != nil {
s.debug.Println(err.Error())
return
}
// Data
w, err := c.Data()
if err != nil {
s.debug.Println(err.Error())
return
}
_, err = w.Write(msg)
if err != nil {
s.debug.Println(err.Error())
return
}
err = w.Close()
if err != nil {
s.debug.Println(err.Error())
return
}
c.Quit()
return nil
}
func sendWelcomeEmail() error {
return nil
}
func parseMailTemplate(tpl string, vals map[string]string) string {
body := SMTPTemplates[tpl]
for oVal, nVal := range vals {
body = strings.Replace(body, oVal, nVal, -1)
}
return body
}