2021-04-10 01:07:13 +03:00
|
|
|
package mail
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-04-10 11:48:06 +03:00
|
|
|
"fmt"
|
2021-04-10 01:07:13 +03:00
|
|
|
"github.com/emersion/go-sasl"
|
|
|
|
"github.com/emersion/go-smtp"
|
|
|
|
conf "github.com/muety/wakapi/config"
|
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
"io"
|
2021-04-10 11:48:06 +03:00
|
|
|
"time"
|
2021-04-10 01:07:13 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type SMTPMailService struct {
|
2021-04-10 11:48:06 +03:00
|
|
|
publicUrl string
|
2021-04-12 23:57:52 +03:00
|
|
|
config conf.SMTPMailConfig
|
2021-04-10 11:48:06 +03:00
|
|
|
auth sasl.Client
|
2021-04-10 01:07:13 +03:00
|
|
|
}
|
|
|
|
|
2021-04-12 23:57:52 +03:00
|
|
|
func NewSMTPMailService(config conf.SMTPMailConfig, publicUrl string) *SMTPMailService {
|
2021-04-10 01:07:13 +03:00
|
|
|
return &SMTPMailService{
|
2021-04-10 11:48:06 +03:00
|
|
|
publicUrl: publicUrl,
|
|
|
|
config: config,
|
2021-04-10 01:07:13 +03:00
|
|
|
auth: sasl.NewPlainClient(
|
|
|
|
"",
|
|
|
|
config.Username,
|
|
|
|
config.Password,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-10 11:48:06 +03:00
|
|
|
func (s *SMTPMailService) SendPasswordReset(recipient *models.User, resetLink string) error {
|
|
|
|
template, err := getPasswordResetTemplate(PasswordResetTplData{ResetLink: resetLink})
|
2021-04-10 01:07:13 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(s.config.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectPasswordReset,
|
|
|
|
}
|
|
|
|
mail.WithHTML(template.String())
|
|
|
|
|
2021-04-10 11:48:06 +03:00
|
|
|
return s.send(s.config.ConnStr(), s.config.TLS, s.auth, mail.From.Raw(), mail.To.RawStrings(), mail.Reader())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SMTPMailService) SendImportNotification(recipient *models.User, duration time.Duration, numHeartbeats int) error {
|
|
|
|
template, err := getImportNotificationTemplate(ImportNotificationTplData{
|
|
|
|
PublicUrl: s.publicUrl,
|
|
|
|
Duration: fmt.Sprintf("%.0f seconds", duration.Seconds()),
|
|
|
|
NumHeartbeats: numHeartbeats,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(s.config.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectImportNotification,
|
|
|
|
}
|
|
|
|
mail.WithHTML(template.String())
|
|
|
|
|
|
|
|
return s.send(s.config.ConnStr(), s.config.TLS, s.auth, mail.From.Raw(), mail.To.RawStrings(), mail.Reader())
|
2021-04-10 01:07:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SMTPMailService) send(addr string, tls bool, a sasl.Client, from string, to []string, r io.Reader) error {
|
|
|
|
dial := smtp.Dial
|
|
|
|
if tls {
|
|
|
|
dial = func(addr string) (*smtp.Client, error) {
|
|
|
|
return smtp.DialTLS(addr, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := dial(addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer c.Close()
|
|
|
|
|
|
|
|
if ok, _ := c.Extension("STARTTLS"); ok {
|
|
|
|
if err = c.StartTLS(nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if a != nil {
|
|
|
|
if ok, _ := c.Extension("AUTH"); !ok {
|
|
|
|
return errors.New("smtp: server doesn't support AUTH")
|
|
|
|
}
|
|
|
|
if err = c.Auth(a); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err = c.Mail(from, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, addr := range to {
|
|
|
|
if err = c.Rcpt(addr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w, err := c.Data()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = io.Copy(w, r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = w.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.Quit()
|
|
|
|
}
|