This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
summer-utils/mailer/mailer.go

130 lines
3.0 KiB
Go

package mailer
import (
"bytes"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"github.com/emersion/go-message"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"io"
"os"
"strings"
)
type Mailer struct {
auth sasl.Client
conf SmtpConfig
pool *x509.CertPool
}
func NewMailer(conf SmtpConfig) (*Mailer, error) {
var pool *x509.CertPool = nil
if conf.Cert != "" {
pool = x509.NewCertPool()
open, err := os.Open(conf.Cert)
if err != nil {
return nil, err
}
all, err := io.ReadAll(open)
if err != nil {
return nil, err
}
if !pool.AppendCertsFromPEM(all) {
return nil, errors.New("invalid SMTP certificate")
}
}
return &Mailer{
auth: sasl.NewPlainClient("", conf.Username, conf.Password),
conf: conf,
pool: pool,
}, nil
}
func (m *Mailer) MailHtml(subject string, to []string, body io.Reader) error {
h := message.Header{}
h.SetContentType("multipart/alternative", nil)
h.SetText("From", m.conf.From)
h.SetText("To", strings.Join(to, ", "))
h.SetText("Subject", subject)
h.SetText("MIME-version", "1.0")
msg := new(bytes.Buffer)
w, err := message.CreateWriter(msg, h)
if err != nil {
return fmt.Errorf("failed to create message writer: %w", err)
}
h2 := message.Header{}
h2.SetContentType("text/html", map[string]string{"charset": "UTF-8"})
part, err := w.CreatePart(h2)
if err != nil {
return fmt.Errorf("failed to create part: %w", err)
}
_, err = io.Copy(part, body)
if err != nil {
return fmt.Errorf("failed to copy body into part: %w", err)
}
err = part.Close()
if err != nil {
return fmt.Errorf("failed to close part: %w", err)
}
err = w.Close()
if err != nil {
return fmt.Errorf("failed to close message writer: %w", err)
}
return m.sendMail(to, msg)
}
func (m *Mailer) sendMail(to []string, body io.Reader) error {
var err error
var client *smtp.Client
if m.conf.Tls {
client, err = smtp.DialTLS(m.conf.Server, &tls.Config{RootCAs: m.pool})
if err != nil {
return fmt.Errorf("failed to dial tls: %w", err)
}
} else {
client, err = smtp.Dial(m.conf.Server)
if err != nil {
return fmt.Errorf("failed to dial: %w", err)
}
}
if m.conf.StartTls {
if ok, _ := client.Extension("STARTTLS"); ok {
if err = client.StartTLS(nil); err != nil {
return fmt.Errorf("failed to start tls: %w", err)
}
}
}
if err = client.Auth(m.auth); err != nil {
return fmt.Errorf("failed to add auth: %w", err)
}
if err = client.Mail(m.conf.Username, nil); err != nil {
return fmt.Errorf("failed to start mail: %w", err)
}
for _, i := range to {
if err = client.Rcpt(i); err != nil {
return fmt.Errorf("failed to add recipient: %w", err)
}
}
wc, err := client.Data()
if err != nil {
return fmt.Errorf("failed to start mail data: %w", err)
}
if _, err = io.Copy(wc, body); err != nil {
return fmt.Errorf("failed to : %w", err)
}
if err = wc.Close(); err != nil {
return fmt.Errorf("failed to close: %w", err)
}
if err = client.Quit(); err != nil {
return fmt.Errorf("failed to quit: %w", err)
}
return nil
}