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
|
package main
import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"net/smtp"
"os"
"path/filepath"
"strings"
"time"
)
type Mailer struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
From string `json:"from"`
StartTLS bool `json:"starttls"`
Insecure bool `json:"insecure"`
}
func loadMailer(root string) *Mailer {
b, err := os.ReadFile(filepath.Join(root, "smtp.json"))
if err != nil {
return nil
}
var m Mailer
if json.Unmarshal(b, &m) != nil || m.Host == "" || m.From == "" {
return nil
}
if m.Port == 0 {
m.Port = 465
}
return &m
}
func (m *Mailer) Send(to, subject, body string) error {
if m == nil {
return errors.New("mail is not configured")
}
addr := net.JoinHostPort(m.Host, fmt.Sprint(m.Port))
tlsConfig := &tls.Config{ServerName: m.Host, InsecureSkipVerify: m.Insecure}
var client *smtp.Client
var err error
if m.StartTLS {
client, err = smtp.Dial(addr)
if err == nil {
err = client.StartTLS(tlsConfig)
}
} else if m.Insecure && m.Port != 465 {
client, err = smtp.Dial(addr)
} else {
var conn net.Conn
conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 15 * time.Second}, "tcp", addr, tlsConfig)
if err == nil {
client, err = smtp.NewClient(conn, m.Host)
}
}
if err != nil {
return err
}
defer client.Close()
if m.User != "" {
if err := client.Auth(smtp.PlainAuth("", m.User, m.Password, m.Host)); err != nil {
return err
}
}
if err := client.Mail(m.From); err != nil {
return err
}
if err := client.Rcpt(to); err != nil {
return err
}
w, err := client.Data()
if err != nil {
return err
}
msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n%s\r\n",
m.From, to, strings.ReplaceAll(subject, "\n", " "), time.Now().Format(time.RFC1123Z), body)
if _, err := w.Write([]byte(msg)); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
return client.Quit()
}
func (s *Server) notify(to, subject, body string) {
if to == "" || s.mailer == nil {
return
}
go func() {
if err := s.mailer.Send(to, subject, body); err != nil {
log.Printf("mail to %s: %v", to, err)
}
}()
}
|