dethnote/src/mail.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())
}