From 54c465281496ec09030863d62e0a1b81eb8b1dbc Mon Sep 17 00:00:00 2001 From: "Arnaud (Arhuman) ASSAD" Date: Tue, 10 Oct 2023 14:31:53 +0200 Subject: [PATCH] Add emailclient --- emailclient/emailclient.go | 199 +++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 emailclient/emailclient.go diff --git a/emailclient/emailclient.go b/emailclient/emailclient.go new file mode 100644 index 0000000..b4c3b8c --- /dev/null +++ b/emailclient/emailclient.go @@ -0,0 +1,199 @@ +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() +}