From 87be137b881ab868df30aa7e35923d6f14606c79 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 18 Dec 2014 13:34:30 +0200 Subject: [PATCH] Rewrite of SendMail function The SendMail function is rewritten and has the following new functionality: - It is optional to skip verification of keys. The config option SKIP_VERIFY is added - If the port is 465, or ending on 465, the TLS(/SSL) connection is started first. --- conf/app.ini | 3 +++ modules/mailer/mailer.go | 55 +++++++++++++++++++++++++++----------- modules/setting/setting.go | 10 ++++--- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/conf/app.ini b/conf/app.ini index 1c7c70bef..d6ee6ae4d 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -94,7 +94,10 @@ SUBJECT = %(APP_NAME)s ; Mail server ; Gmail: smtp.gmail.com:587 ; QQ: smtp.qq.com:25 +; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used. HOST = +; Do not verify the certificate of the server. Only use this for self-signed certificates +SKIP_VERIFY = ; Mail from address FROM = ; Mailer user name and password diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index 474e1481c..6d75fa3f2 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -67,22 +67,53 @@ func processMailQueue() { } // sendMail allows mail with self-signed certificates. -func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipients []string, msgContent []byte) error { - client, err := smtp.Dial(hostAddressWithPort) +func sendMail(settings *setting.Mailer, from string, recipients []string, msgContent []byte) error { + host, port, err := net.SplitHostPort(settings.Host) if err != nil { return err } - host, _, _ := net.SplitHostPort(hostAddressWithPort) - tlsConn := &tls.Config{ - InsecureSkipVerify: true, + if len(port) == 0 { + port = "587" + } + + tlsconfig := &tls.Config{ + InsecureSkipVerify: settings.SkipVerify, ServerName: host, } - if err = client.StartTLS(tlsConn); err != nil { + + var conn net.Conn + if conn, err = net.Dial("tcp", net.JoinHostPort(host, port)); err != nil { + return err + } + defer conn.Close() + + connection_secure := false + // Start TLS directly if the port ends with 465 (SMTPS protocol) + if strings.HasSuffix(port, "465") { + conn = tls.Client(conn, tlsconfig) + connection_secure = true + } + + var client *smtp.Client + if client, err = smtp.NewClient(conn, host); err != nil { return err } - if ok, _ := client.Extension("AUTH"); ok && auth != nil { + // If not using SMTPS, alway use STARTTLS if available + has_starttls, _ := client.Extension("STARTTLS") + if !connection_secure && has_starttls { + if err = client.StartTLS(tlsconfig); err != nil { + return err + } + } + + auth_available, _ := client.Extension("AUTH") + + // Possible improvement: only plain authentication is now available. + // Maybe in future CRAM MD5 as well? + if auth_available && len(settings.User) > 0 { + auth := smtp.PlainAuth("", settings.User, settings.Passwd, host) if err = client.Auth(auth); err != nil { return err } @@ -116,7 +147,6 @@ func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipient // Direct Send mail message func Send(msg *Message) (int, error) { log.Trace("Sending mails to: %s", strings.Join(msg.To, "; ")) - host := strings.Split(setting.MailService.Host, ":") // get message body content := msg.Content() @@ -127,17 +157,12 @@ func Send(msg *Message) (int, error) { return 0, fmt.Errorf("empty email body") } - var auth smtp.Auth - if len(setting.MailService.Passwd) > 0 { - auth = smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0]) - } - if msg.Massive { // send mail to multiple emails one by one num := 0 for _, to := range msg.To { body := []byte("To: " + to + "\r\n" + content) - err := sendMail(setting.MailService.Host, auth, msg.From, []string{to}, body) + err := sendMail(setting.MailService, msg.From, []string{to}, body) if err != nil { return num, err } @@ -148,7 +173,7 @@ func Send(msg *Message) (int, error) { body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content) // send to multiple emails in one message - err := sendMail(setting.MailService.Host, auth, msg.From, msg.To, body) + err := sendMail(setting.MailService, msg.From, msg.To, body) if err != nil { return 0, err } else { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 7765a859a..76e1ab769 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -437,6 +437,7 @@ type Mailer struct { Host string From string User, Passwd string + SkipVerify bool } type OauthInfo struct { @@ -463,10 +464,11 @@ func newMailService() { } MailService = &Mailer{ - Name: Cfg.MustValue("mailer", "NAME", AppName), - Host: Cfg.MustValue("mailer", "HOST"), - User: Cfg.MustValue("mailer", "USER"), - Passwd: Cfg.MustValue("mailer", "PASSWD"), + Name: Cfg.MustValue("mailer", "NAME", AppName), + Host: Cfg.MustValue("mailer", "HOST"), + User: Cfg.MustValue("mailer", "USER"), + Passwd: Cfg.MustValue("mailer", "PASSWD"), + SkipVerify: Cfg.MustBool("mailer", "SKIP_VERIFY", false), } MailService.From = Cfg.MustValue("mailer", "FROM", MailService.User) log.Info("Mail Service Enabled")