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) } }() }