package emailclient import ( "crypto/tls" "errors" "fmt" "net" "net/mail" "net/smtp" log "github.com/sirupsen/logrus" ) // SMTPCredentials is a struc holding the information needed to connect to a SMTP server type SMTPCredentials struct { Host string Email string Password string } // emailClient is a struct holding the information requiered to send with SMTP type emailClient struct { client *smtp.Client } // EmailClient is the generic interface for all client sending emails type EmailClient interface { SendMsg(headers map[string]string, body string) } // NewSMTPClient (host, user, password) returns a SMTPClient // // host is the SMTP server used to connect // port is the SMTP port used to connect // user is usually the email address use to connect to SMTP // password is the associated password func NewSMTPClient(host string, port string, user string, password string) (EmailClient, error) { var ec emailClient log.Infof("user: (%s) pass: (%s)", user, password) auth := smtp.PlainAuth("", user, password, host) // TLS config tlsconfig := &tls.Config{ InsecureSkipVerify: false, ServerName: host, } // Here is the key, you need to call tls.Dial instead of smtp.Dial // for smtp servers running on 465 that require an ssl connection // from the very beginning (no starttls) var conn *tls.Conn var c *smtp.Client var err error if port == "465" { conn, err = tls.Dial("tcp", host+":"+port, tlsconfig) if err != nil { log.Error("[sendMail] ERROR while dialing: " + err.Error()) return ec, err } c, err = smtp.NewClient(conn, host) if err != nil { log.Error("[sendMail] ERROR while NewClient: " + err.Error()) return ec, err } } else { c, err = smtp.Dial(host + ":" + port) if err != nil { log.Error("[sendMail] ERROR while Dialing: " + err.Error()) return ec, err } err = c.StartTLS(tlsconfig) if err != nil { log.Error("[sendMail] ERROR while StartTLS: " + err.Error()) return ec, err } } // defer c.Quit() // defer c.Close() ec.client = c // Auth if err = ec.client.Auth(auth); err != nil { log.Error("[sendMail] ERROR while Auth: " + err.Error()) return ec, err } return ec, nil } // NewGmailClient returns a GmailClient func NewGmailClient(email, pass string) (EmailClient, error) { var ec emailClient // cred.Host = "smtp.gmail.com:587" echost := "smtp.gmail.com:465" if pass == "" { log.Error("Email password missing") return ec, errors.New("Email password missing") } host, _, _ := net.SplitHostPort(echost) auth := smtp.PlainAuth("", email, pass, host) // TLS config tlsconfig := &tls.Config{ InsecureSkipVerify: true, ServerName: host, } // Cavehat, you need to call tls.Dial instead of smtp.Dial // for smtp servers running on 465 that require an ssl connection // from the very beginning (no starttls) conn, err := tls.Dial("tcp", echost, tlsconfig) if err != nil { log.Error(fmt.Sprintf("Dial error: " + err.Error())) return ec, err } client, err := smtp.NewClient(conn, host) if err != nil { log.Error(fmt.Sprintf("NewClient error: " + err.Error())) return ec, err } ec.client = client // Auth if err = ec.client.Auth(auth); err != nil { log.Error(fmt.Sprintf("Auth error: %s (%s)", err, host)) return ec, err } return ec, nil } // SendMsg send a message (email) to rcpt which is in the 'headers' parameter ('To' key) and whose content is the 'body' parameter. // Ohter keys of interest in headers are 'Subject' and 'MIME-version' func (ec emailClient) SendMsg(headers map[string]string, body string) { from := mail.Address{Name: "", Address: headers["From"]} to := mail.Address{Name: "", Address: headers["To"]} // headers["To"] = to.String() if _, ok := headers["Subject"]; ok { headers["Subject"] = "Subject unknown" } log.Printf("from = %s", headers["From"]) log.Printf("to = %s", headers["To"]) log.Printf("subj = %s", headers["Subject"]) // Setup message message := "" for k, v := range headers { message += fmt.Sprintf("%s: %s\r\n", k, v) } message += "\r\n" + body c := ec.client // To && From if err := c.Mail(from.Address); err != nil { log.Error("Can't set From: " + err.Error()) return } if err := c.Rcpt(to.Address); err != nil { log.Error("Can't set To: " + err.Error()) return } // Data w, err := c.Data() if err != nil { log.Error("Can't set Data: " + err.Error()) return } _, err = w.Write([]byte(message)) if err != nil { log.Error("Can't write Data: " + err.Error()) return } err = w.Close() if err != nil { log.Error("Can't Close: " + err.Error()) return } err = c.Quit() if err != nil { log.Error("Can't Quit: " + err.Error()) return } c.Close() }