126 lines
2.8 KiB
Go
126 lines
2.8 KiB
Go
package dethnote
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"net/mail"
|
|
"net/smtp"
|
|
"net/textproto"
|
|
"regexp"
|
|
"strconv"
|
|
)
|
|
|
|
var smtpURLRegexp = regexp.MustCompile(
|
|
`^` + // bind to beginning
|
|
`(?P<proto>(?:ssl|smtps)://)?` + // optional ssl:// protocol prefix
|
|
`(?:(?P<user>[^:]+):` + // `username:password@` syntax either
|
|
`(?P<pass>[^@]+)@)?` + // must exist or be entirely absent.
|
|
`(?P<host>[^:]+):?` + // `host[:port]` standard case
|
|
`(?P<port>[0-9]+)?` +
|
|
`$`,
|
|
)
|
|
|
|
// MailConfig holds SMTP settings.
|
|
type MailConfig struct {
|
|
Username string
|
|
Password string
|
|
Hostname string
|
|
Port int
|
|
SSL bool
|
|
}
|
|
|
|
func (m MailConfig) String() string {
|
|
username := ""
|
|
if m.Username != "" {
|
|
username = fmt.Sprintf("'%s'@", m.Username)
|
|
}
|
|
return fmt.Sprintf("%s%s:%d", username, m.Hostname, m.Port)
|
|
}
|
|
|
|
// ParseSMTPUrl validates and parses out the SMTP URL.
|
|
func ParseSMTPUrl(url string) (MailConfig, error) {
|
|
|
|
m := smtpURLRegexp.FindStringSubmatch(url)
|
|
if len(m) == 0 {
|
|
return MailConfig{}, errors.New("failed to match regexp")
|
|
}
|
|
|
|
ssl := m[1] != ""
|
|
port, _ := strconv.Atoi(m[5])
|
|
if port == 0 && ssl {
|
|
port = 465
|
|
} else if port == 0 {
|
|
port = 25
|
|
}
|
|
|
|
return MailConfig{
|
|
Username: m[2],
|
|
Password: m[3],
|
|
Hostname: m[4],
|
|
Port: port,
|
|
SSL: ssl,
|
|
}, nil
|
|
}
|
|
|
|
// Mail is an email message to be delivered.
|
|
type Mail struct {
|
|
From string
|
|
To string
|
|
Subject string
|
|
HTML string
|
|
}
|
|
|
|
// SetSMTP sets the SMTP config by URL.
|
|
func (s *Server) SetSMTP(url string) error {
|
|
config, err := ParseSMTPUrl(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.smtp = config
|
|
Log.Info("Email server configuration: %s", config)
|
|
return nil
|
|
}
|
|
|
|
// SendMail sends an email out.
|
|
func (s *Server) SendMail(m Mail) error {
|
|
// Server config.
|
|
cfg := s.smtp
|
|
|
|
// validate from address
|
|
from, err := mail.ParseAddress(m.From)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// validate to address
|
|
to, err := mail.ParseAddress(m.To)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// set headers for html email
|
|
header := textproto.MIMEHeader{}
|
|
header.Set(textproto.CanonicalMIMEHeaderKey("from"), from.Address)
|
|
header.Set(textproto.CanonicalMIMEHeaderKey("to"), to.Address)
|
|
header.Set(textproto.CanonicalMIMEHeaderKey("content-type"), "text/html; charset=UTF-8")
|
|
header.Set(textproto.CanonicalMIMEHeaderKey("mime-version"), "1.0")
|
|
header.Set(textproto.CanonicalMIMEHeaderKey("subject"), m.Subject)
|
|
|
|
// init empty message
|
|
var buffer bytes.Buffer
|
|
|
|
// write header
|
|
for key, value := range header {
|
|
buffer.WriteString(fmt.Sprintf("%s: %s\r\n", key, value[0]))
|
|
}
|
|
|
|
// write body
|
|
buffer.WriteString(fmt.Sprintf("\r\n%s", m.HTML))
|
|
|
|
// send email
|
|
addr := fmt.Sprintf("%s:%d", cfg.Hostname, cfg.Port)
|
|
auth := smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Hostname)
|
|
return smtp.SendMail(addr, auth, from.Address, []string{to.Address}, buffer.Bytes())
|
|
}
|