package dethnote import ( "bytes" "errors" "fmt" "net/mail" "net/smtp" "net/textproto" "regexp" "strconv" ) var smtpURLRegexp = regexp.MustCompile( `^` + // bind to beginning `(?P(?:ssl|smtps)://)?` + // optional ssl:// protocol prefix `(?:(?P[^:]+):` + // `username:password@` syntax either `(?P[^@]+)@)?` + // must exist or be entirely absent. `(?P[^:]+):?` + // `host[:port]` standard case `(?P[0-9]+)?` + `$`, ) // MailConfig holds SMTP settings. type MailConfig struct { Username string Password string Hostname string Port int SSL bool } func (m MailConfig) String() string { username := "" if m.Username != "" { username = fmt.Sprintf("'%s'@", m.Username) } return fmt.Sprintf("%s%s:%d", username, m.Hostname, m.Port) } // ParseSMTPUrl validates and parses out the SMTP URL. func ParseSMTPUrl(url string) (MailConfig, error) { m := smtpURLRegexp.FindStringSubmatch(url) if len(m) == 0 { return MailConfig{}, errors.New("failed to match regexp") } ssl := m[1] != "" port, _ := strconv.Atoi(m[5]) if port == 0 && ssl { port = 465 } else if port == 0 { port = 25 } return MailConfig{ Username: m[2], Password: m[3], Hostname: m[4], Port: port, SSL: ssl, }, nil } // Mail is an email message to be delivered. type Mail struct { From string To string Subject string HTML string } // SetSMTP sets the SMTP config by URL. func (s *Server) SetSMTP(url string) error { config, err := ParseSMTPUrl(url) if err != nil { return err } s.smtp = config Log.Info("Email server configuration: %s", config) return nil } // SendMail sends an email out. func (s *Server) SendMail(m Mail) error { // Server config. cfg := s.smtp // validate from address from, err := mail.ParseAddress(m.From) if err != nil { return err } // validate to address to, err := mail.ParseAddress(m.To) if err != nil { return err } // set headers for html email header := textproto.MIMEHeader{} header.Set(textproto.CanonicalMIMEHeaderKey("from"), from.Address) header.Set(textproto.CanonicalMIMEHeaderKey("to"), to.Address) header.Set(textproto.CanonicalMIMEHeaderKey("content-type"), "text/html; charset=UTF-8") header.Set(textproto.CanonicalMIMEHeaderKey("mime-version"), "1.0") header.Set(textproto.CanonicalMIMEHeaderKey("subject"), m.Subject) // init empty message var buffer bytes.Buffer // write header for key, value := range header { buffer.WriteString(fmt.Sprintf("%s: %s\r\n", key, value[0])) } // write body buffer.WriteString(fmt.Sprintf("\r\n%s", m.HTML)) // send email addr := fmt.Sprintf("%s:%d", cfg.Hostname, cfg.Port) auth := smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Hostname) return smtp.SendMail(addr, auth, from.Address, []string{to.Address}, buffer.Bytes()) }