Files
Andy Dixon 680bfac014 Send as webhook.me.uk, add favicon, share image and SEO tags
Mail now comes from no-reply@webhook.me.uk (DKIM, SPF and DMARC are
set up for it) and the Message-ID uses that domain instead of the
container hostname. The landing page gets a favicon, an Open Graph
image, canonical/OG/Twitter/JSON-LD tags, robots.txt that keeps
crawlers off email URLs, and a sitemap. Footer links to dixon.cx.
2026-09-22 08:50:20 +01:00

69 lines
1.6 KiB
Go

package main
import (
"fmt"
"mime"
"mime/quotedprintable"
"net/smtp"
"os"
"strings"
"time"
)
var (
smtpAddr = envOr("SMTP_ADDR", "127.0.0.1:25")
mailFrom = envOr("MAIL_FROM", "no-reply@webhook.me.uk")
)
// sendMail is a variable so tests can capture messages instead of sending them.
var sendMail = smtpSend
func smtpSend(to, subject, htmlBody string) error {
c, err := smtp.Dial(smtpAddr)
if err != nil {
return err
}
defer c.Close()
if err := c.Mail(mailFrom); err != nil {
return err
}
if err := c.Rcpt(to); err != nil {
return err
}
w, err := c.Data()
if err != nil {
return err
}
if _, err := w.Write(buildMessage(to, subject, htmlBody)); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
return c.Quit()
}
func buildMessage(to, subject, htmlBody string) []byte {
host := mailFrom[strings.LastIndex(mailFrom, "@")+1:]
var b []byte
b = fmt.Appendf(b, "From: Webhook Call <%s>\r\n", mailFrom)
b = fmt.Appendf(b, "To: <%s>\r\n", to)
b = fmt.Appendf(b, "Subject: %s\r\n", mime.QEncoding.Encode("utf-8", subject))
b = fmt.Appendf(b, "Date: %s\r\n", time.Now().Format(time.RFC1123Z))
b = fmt.Appendf(b, "Message-ID: <%d.%d@%s>\r\n", time.Now().UnixNano(), os.Getpid(), host)
b = append(b, "MIME-Version: 1.0\r\n"...)
b = append(b, "Content-Type: text/html; charset=UTF-8\r\n"...)
b = append(b, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"...)
qp := quotedprintable.NewWriter(sliceWriter{&b})
_, _ = qp.Write([]byte(htmlBody))
_ = qp.Close()
return b
}
type sliceWriter struct{ b *[]byte }
func (s sliceWriter) Write(p []byte) (int, error) {
*s.b = append(*s.b, p...)
return len(p), nil
}