package alerts import ( "crypto/tls" "fmt" "net/smtp" "strings" ) func sendMail(addr, user, pass, from string, to []string, subject, body string) error { host := addr for i, c := range addr { if c == ':' { host = addr[:i] break } } msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s", from, strings.Join(to, ", "), subject, body) auth := newLoginAuth(user, pass) c, err := smtp.Dial(addr) if err != nil { return fmt.Errorf("dial: %w", err) } defer c.Close() if err := c.StartTLS(&tls.Config{ServerName: host}); err != nil { return fmt.Errorf("starttls: %w", err) } if user != "" { if err := c.Auth(auth); err != nil { return fmt.Errorf("auth: %w", err) } } if err := c.Mail(from); err != nil { return fmt.Errorf("mail from: %w", err) } for _, addr := range to { if err := c.Rcpt(addr); err != nil { return fmt.Errorf("rcpt %s: %w", addr, err) } } w, err := c.Data() if err != nil { return fmt.Errorf("data: %w", err) } if _, err := fmt.Fprint(w, msg); err != nil { return fmt.Errorf("write: %w", err) } if err := w.Close(); err != nil { return fmt.Errorf("close: %w", err) } return c.Quit() } type loginAuth struct { user, pass string } func newLoginAuth(user, pass string) smtp.Auth { return &loginAuth{user, pass} } func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { return "LOGIN", []byte(a.user), nil } func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { if more { msg := string(fromServer) switch { case strings.EqualFold(msg, "Username:"): return []byte(a.user), nil case strings.EqualFold(msg, "Password:"): return []byte(a.pass), nil } } return nil, nil }