-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmail.go
133 lines (123 loc) · 3.6 KB
/
mail.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) 2023 Julian Müller (ChaoticByte)
package main
import (
"crypto/tls"
"encoding/base64"
"fmt"
"net/mail"
"net/smtp"
"time"
)
const MAIL_LINE_SEP = "\r\n"
type MailContent struct {
Subject string
Body string
}
func (c MailContent) serializeValidMail(from string, to string) []byte {
// We'll send base64 encoded Subject & Body, because we Dschörmäns have umlauts
// and I'm too lazy to encode ä into =E4 and so on
subjectEncoded := base64.StdEncoding.EncodeToString([]byte(c.Subject))
bodyEncoded := base64.StdEncoding.EncodeToString([]byte(c.Body))
data := []byte(fmt.Sprintf(
"Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: base64\r\nFrom: %v%vTo: %v%vSubject: =?utf-8?b?%v?=%v%v%v",
from, MAIL_LINE_SEP,
to, MAIL_LINE_SEP,
subjectEncoded, MAIL_LINE_SEP,
MAIL_LINE_SEP,
bodyEncoded))
// done, I guess
return data
}
type Recipient struct {
Address string `json:"address"`
// Must be a configured filter id
Filters []Filter `json:"include"`
}
type SmtpSettings struct {
From string `json:"from"`
ServerHost string `json:"host"`
ServerPort int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
}
func (r Recipient) sendNotices(notices []WidNotice, template MailTemplate, auth smtp.Auth, smtpConfig SmtpSettings, cache *map[string][]byte) error {
logger.debug("Generating and sending mails to " + r.Address + " ...")
cacheHits := 0
cacheMisses := 0
mails := [][]byte{}
for _, n := range notices {
var data []byte
cacheResult := (*cache)[n.Uuid]
if len(cacheResult) > 0 {
cacheHits++
data = cacheResult
} else {
cacheMisses++
mailContent, err := template.generate(TemplateData{n, Version})
if err != nil {
logger.error("Could not create mail from template")
logger.error(err)
}
// serialize & send mail
data = mailContent.serializeValidMail(smtpConfig.From, r.Address)
// add to cache
(*cache)[n.Uuid] = data
}
mails = append(mails, data)
}
logger.debug(fmt.Sprintf("%v mail cache hits, %v misses", cacheHits, cacheMisses))
err := sendMails(
smtpConfig,
auth,
r.Address,
mails,
)
if err != nil { return err }
logger.debug("Successfully sent all mails to " + r.Address)
return nil
}
func sendMails(smtpConf SmtpSettings, auth smtp.Auth, to string, data [][]byte) error {
addr := fmt.Sprintf("%v:%v", smtpConf.ServerHost, smtpConf.ServerPort)
logger.debug("Connecting to mail server at " + addr + " ...")
connection, err := smtp.Dial(addr)
if err != nil { return err }
defer connection.Close()
// can leave out connection.Hello
hasTlsExt, _ := connection.Extension("starttls")
if hasTlsExt {
err = connection.StartTLS(&tls.Config{ServerName: smtpConf.ServerHost})
if err != nil { return err }
logger.debug("Mail Server supports TLS")
} else {
logger.debug("Mail Server doesn't support TLS")
}
logger.debug("Authenticating to mail server ...")
err = connection.Auth(auth)
if err != nil { return err }
if logger.LogLevel >= 3 {
fmt.Printf("DEBUG %v Sending mails to server ", time.Now().Format("2006/01/02 15:04:05.000000"))
}
for _, d := range data {
err = connection.Mail(smtpConf.From)
if err != nil { return err }
err = connection.Rcpt(to)
if err != nil { return err }
writer, err := connection.Data()
if err != nil { return err }
_, err = writer.Write(d)
if err != nil { return err }
err = writer.Close()
if err != nil { return err }
if logger.LogLevel >= 3 {
print(".")
}
}
if logger.LogLevel >= 3 {
print("\n")
}
return connection.Quit()
}
func mailAddressIsValid(address string) bool {
_, err := mail.ParseAddress(address);
return err == nil
}