package mail import ( "bytes" "crypto/tls" "fmt" "net" "net/smtp" "strings" ) // ContactEmail sends a contact form message via the configured SMTP relay. func (c Config) ContactEmail(name, replyTo, message string) error { subject := fmt.Sprintf("Contact form: %s", name) var body bytes.Buffer body.WriteString(fmt.Sprintf("Name: %s\n", name)) body.WriteString(fmt.Sprintf("Email: %s\n\n", replyTo)) body.WriteString(message) return c.send(subject, body.String(), replyTo) } func (c Config) send(subject, body, replyTo string) error { addr := fmt.Sprintf("%s:%d", c.Host, c.Port) var msg bytes.Buffer msg.WriteString(fmt.Sprintf("From: %s\r\n", c.From)) msg.WriteString(fmt.Sprintf("To: %s\r\n", c.To)) if replyTo != "" { msg.WriteString(fmt.Sprintf("Reply-To: %s\r\n", replyTo)) } msg.WriteString(fmt.Sprintf("Subject: %s\r\n", subject)) msg.WriteString("MIME-Version: 1.0\r\n") msg.WriteString("Content-Type: text/plain; charset=UTF-8\r\n") msg.WriteString("\r\n") msg.WriteString(body) raw := msg.Bytes() from := extractAddr(c.From) var auth smtp.Auth if c.User != "" { auth = smtp.PlainAuth("", c.User, c.Password, c.Host) } switch c.tlsMode() { case "plain": return smtp.SendMail(addr, auth, from, []string{c.To}, raw) case "tls": return c.sendTLS(addr, auth, from, raw) default: return c.sendStartTLS(addr, auth, from, raw) } } func (c Config) tlsMode() string { switch strings.ToLower(c.TLS) { case "plain", "tls": return strings.ToLower(c.TLS) } if c.Port == 465 { return "tls" } if c.Port == 25 { return "plain" } return "starttls" } func (c Config) sendStartTLS(addr string, auth smtp.Auth, from string, raw []byte) error { conn, err := net.Dial("tcp", addr) if err != nil { return err } defer conn.Close() client, err := smtp.NewClient(conn, c.Host) if err != nil { return err } defer client.Close() if ok, _ := client.Extension("STARTTLS"); ok { if err := client.StartTLS(&tls.Config{ServerName: c.Host}); err != nil { return err } } if auth != nil { if ok, _ := client.Extension("AUTH"); ok { if err := client.Auth(auth); err != nil { return err } } } if err := client.Mail(from); err != nil { return err } if err := client.Rcpt(c.To); err != nil { return err } w, err := client.Data() if err != nil { return err } if _, err := w.Write(raw); err != nil { return err } if err := w.Close(); err != nil { return err } return client.Quit() } func (c Config) sendTLS(addr string, auth smtp.Auth, from string, raw []byte) error { conn, err := tls.Dial("tcp", addr, &tls.Config{ServerName: c.Host}) if err != nil { return err } defer conn.Close() client, err := smtp.NewClient(conn, c.Host) if err != nil { return err } defer client.Close() if auth != nil { if ok, _ := client.Extension("AUTH"); ok { if err := client.Auth(auth); err != nil { return err } } } if err := client.Mail(from); err != nil { return err } if err := client.Rcpt(c.To); err != nil { return err } w, err := client.Data() if err != nil { return err } if _, err := w.Write(raw); err != nil { return err } if err := w.Close(); err != nil { return err } return client.Quit() } func extractAddr(from string) string { if i := strings.Index(from, "<"); i >= 0 { if j := strings.Index(from[i:], ">"); j > 0 { return strings.TrimSpace(from[i+1 : i+j]) } } return strings.TrimSpace(from) }