2023-10-13 20:02:51 +02:00
|
|
|
// Copyright (c) 2023 Julian Müller (ChaoticByte)
|
|
|
|
|
2023-10-11 22:14:01 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2025-06-12 20:13:08 +02:00
|
|
|
"mime"
|
|
|
|
"mime/quotedprintable"
|
2023-10-11 22:14:01 +02:00
|
|
|
"net/mail"
|
|
|
|
"net/smtp"
|
2025-06-12 20:13:08 +02:00
|
|
|
"strings"
|
2023-10-11 22:14:01 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type MailContent struct {
|
|
|
|
Subject string
|
|
|
|
Body string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c MailContent) serializeValidMail(from string, to string) []byte {
|
2025-06-12 20:13:08 +02:00
|
|
|
// format subject using Q Encoding from RFC2047
|
|
|
|
subjectEncoded := mime.QEncoding.Encode("utf-8", c.Subject)
|
|
|
|
// format body using Quoted-Printable Encoding from RFC2045
|
|
|
|
var bodyEncoded strings.Builder
|
|
|
|
bew := quotedprintable.NewWriter(&bodyEncoded)
|
|
|
|
bew.Write([]byte(c.Body))
|
|
|
|
bew.Close()
|
|
|
|
// glue it all together
|
2025-06-12 00:00:52 +02:00
|
|
|
data := fmt.Appendf(nil,
|
2025-06-12 20:13:08 +02:00
|
|
|
"Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: Quoted-Printable\r\nFrom: %v\r\nTo: %v\r\nSubject: %v\r\n\r\n%v",
|
|
|
|
from, to, subjectEncoded, bodyEncoded.String(),
|
|
|
|
)
|
2023-10-11 22:14:01 +02:00
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2025-06-12 00:00:52 +02:00
|
|
|
type NotifyList struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Recipients []string `json:"recipients"`
|
2023-10-11 22:14:01 +02:00
|
|
|
// Must be a configured filter id
|
2025-06-12 00:00:52 +02:00
|
|
|
Filter []Filter `json:"filter"`
|
2023-10-11 22:14:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type SmtpSettings struct {
|
|
|
|
From string `json:"from"`
|
|
|
|
ServerHost string `json:"host"`
|
|
|
|
ServerPort int `json:"port"`
|
|
|
|
User string `json:"user"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
|
|
|
|
2025-06-12 17:23:08 +02:00
|
|
|
func sendNotices(recipient string, notices []*WidNotice, template MailTemplate, auth smtp.Auth, smtpConfig SmtpSettings, cache *map[string][]byte) error {
|
2025-06-12 00:00:52 +02:00
|
|
|
logger.debug("Generating and sending mails for recipient " + recipient + " ...")
|
2023-10-14 13:26:29 +02:00
|
|
|
cacheHits := 0
|
|
|
|
cacheMisses := 0
|
2023-10-19 21:01:29 +02:00
|
|
|
mails := [][]byte{}
|
2024-07-22 18:41:22 +02:00
|
|
|
for _, n := range notices {
|
2023-10-14 13:26:29 +02:00
|
|
|
var data []byte
|
|
|
|
cacheResult := (*cache)[n.Uuid]
|
|
|
|
if len(cacheResult) > 0 {
|
|
|
|
cacheHits++
|
|
|
|
data = cacheResult
|
|
|
|
} else {
|
|
|
|
cacheMisses++
|
2024-07-22 19:55:44 +02:00
|
|
|
mailContent, err := template.generate(TemplateData{n, Version})
|
2023-10-14 13:26:29 +02:00
|
|
|
if err != nil {
|
|
|
|
logger.error("Could not create mail from template")
|
|
|
|
logger.error(err)
|
|
|
|
}
|
2025-06-11 17:29:14 +02:00
|
|
|
// serialize mail
|
2025-06-12 00:00:52 +02:00
|
|
|
data = mailContent.serializeValidMail(smtpConfig.From, recipient)
|
2023-10-14 13:26:29 +02:00
|
|
|
// add to cache
|
|
|
|
(*cache)[n.Uuid] = data
|
2023-10-11 22:14:01 +02:00
|
|
|
}
|
2023-10-19 21:01:29 +02:00
|
|
|
mails = append(mails, data)
|
2023-10-11 22:14:01 +02:00
|
|
|
}
|
2023-10-14 13:26:29 +02:00
|
|
|
logger.debug(fmt.Sprintf("%v mail cache hits, %v misses", cacheHits, cacheMisses))
|
2023-10-19 21:01:29 +02:00
|
|
|
err := sendMails(
|
|
|
|
smtpConfig,
|
|
|
|
auth,
|
2025-06-12 00:00:52 +02:00
|
|
|
recipient,
|
2023-10-19 21:01:29 +02:00
|
|
|
mails,
|
|
|
|
)
|
2024-07-22 18:41:22 +02:00
|
|
|
if err != nil { return err }
|
2025-06-12 00:00:52 +02:00
|
|
|
logger.debug("Successfully sent all mails to " + recipient)
|
2023-10-11 22:14:01 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func mailAddressIsValid(address string) bool {
|
|
|
|
_, err := mail.ParseAddress(address);
|
|
|
|
return err == nil
|
|
|
|
}
|