github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/utils/mail.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package utils 5 6 import ( 7 "crypto/tls" 8 "mime" 9 "net" 10 "net/mail" 11 "net/smtp" 12 "time" 13 14 "gopkg.in/gomail.v2" 15 16 "net/http" 17 18 "io" 19 20 l4g "github.com/alecthomas/log4go" 21 "github.com/mattermost/html2text" 22 "github.com/mattermost/mattermost-server/model" 23 ) 24 25 func encodeRFC2047Word(s string) string { 26 return mime.BEncoding.Encode("utf-8", s) 27 } 28 29 func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) { 30 var conn net.Conn 31 var err error 32 33 if config.EmailSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 34 tlsconfig := &tls.Config{ 35 InsecureSkipVerify: *config.EmailSettings.SkipServerCertificateVerification, 36 ServerName: config.EmailSettings.SMTPServer, 37 } 38 39 conn, err = tls.Dial("tcp", config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort, tlsconfig) 40 if err != nil { 41 return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.open_tls.app_error", nil, err.Error(), http.StatusInternalServerError) 42 } 43 } else { 44 conn, err = net.Dial("tcp", config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort) 45 if err != nil { 46 return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.open.app_error", nil, err.Error(), http.StatusInternalServerError) 47 } 48 } 49 50 return conn, nil 51 } 52 53 func newSMTPClient(conn net.Conn, config *model.Config) (*smtp.Client, *model.AppError) { 54 c, err := smtp.NewClient(conn, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort) 55 if err != nil { 56 l4g.Error(T("utils.mail.new_client.open.error"), err) 57 return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.open_tls.app_error", nil, err.Error(), http.StatusInternalServerError) 58 } 59 60 hostname := GetHostnameFromSiteURL(*config.ServiceSettings.SiteURL) 61 if hostname != "" { 62 err := c.Hello(hostname) 63 if err != nil { 64 l4g.Error(T("utils.mail.new_client.helo.error"), err) 65 return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.helo.app_error", nil, err.Error(), http.StatusInternalServerError) 66 } 67 } 68 69 if config.EmailSettings.ConnectionSecurity == model.CONN_SECURITY_STARTTLS { 70 tlsconfig := &tls.Config{ 71 InsecureSkipVerify: *config.EmailSettings.SkipServerCertificateVerification, 72 ServerName: config.EmailSettings.SMTPServer, 73 } 74 c.StartTLS(tlsconfig) 75 } 76 77 if *config.EmailSettings.EnableSMTPAuth { 78 auth := smtp.PlainAuth("", config.EmailSettings.SMTPUsername, config.EmailSettings.SMTPPassword, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort) 79 80 if err = c.Auth(auth); err != nil { 81 return nil, model.NewAppError("SendMail", "utils.mail.new_client.auth.app_error", nil, err.Error(), http.StatusInternalServerError) 82 } 83 } 84 return c, nil 85 } 86 87 func TestConnection(config *model.Config) { 88 if !config.EmailSettings.SendEmailNotifications { 89 return 90 } 91 92 conn, err1 := connectToSMTPServer(config) 93 if err1 != nil { 94 l4g.Error(T("utils.mail.test.configured.error"), T(err1.Message), err1.DetailedError) 95 return 96 } 97 defer conn.Close() 98 99 c, err2 := newSMTPClient(conn, config) 100 if err2 != nil { 101 l4g.Error(T("utils.mail.test.configured.error"), T(err2.Message), err2.DetailedError) 102 return 103 } 104 defer c.Quit() 105 defer c.Close() 106 } 107 108 func SendMailUsingConfig(to, subject, htmlBody string, config *model.Config, enableComplianceFeatures bool) *model.AppError { 109 fromMail := mail.Address{Name: config.EmailSettings.FeedbackName, Address: config.EmailSettings.FeedbackEmail} 110 return sendMail(to, to, fromMail, subject, htmlBody, nil, nil, config, enableComplianceFeatures) 111 } 112 113 // allows for sending an email with attachments and differing MIME/SMTP recipients 114 func SendMailUsingConfigAdvanced(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, config *model.Config, enableComplianceFeatures bool) *model.AppError { 115 return sendMail(mimeTo, smtpTo, from, subject, htmlBody, attachments, mimeHeaders, config, enableComplianceFeatures) 116 } 117 118 func sendMail(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, config *model.Config, enableComplianceFeatures bool) *model.AppError { 119 if !config.EmailSettings.SendEmailNotifications || len(config.EmailSettings.SMTPServer) == 0 { 120 return nil 121 } 122 123 l4g.Debug(T("utils.mail.send_mail.sending.debug"), mimeTo, subject) 124 125 htmlMessage := "\r\n<html><body>" + htmlBody + "</body></html>" 126 127 txtBody, err := html2text.FromString(htmlBody) 128 if err != nil { 129 l4g.Warn(err) 130 txtBody = "" 131 } 132 133 headers := map[string][]string{ 134 "From": {from.String()}, 135 "To": {mimeTo}, 136 "Subject": {encodeRFC2047Word(subject)}, 137 "Content-Transfer-Encoding": {"8bit"}, 138 "Auto-Submitted": {"auto-generated"}, 139 "Precedence": {"bulk"}, 140 } 141 if mimeHeaders != nil { 142 for k, v := range mimeHeaders { 143 headers[k] = []string{encodeRFC2047Word(v)} 144 } 145 } 146 147 m := gomail.NewMessage(gomail.SetCharset("UTF-8")) 148 m.SetHeaders(headers) 149 m.SetDateHeader("Date", time.Now()) 150 m.SetBody("text/plain", txtBody) 151 m.AddAlternative("text/html", htmlMessage) 152 153 if attachments != nil { 154 fileBackend, err := NewFileBackend(&config.FileSettings, enableComplianceFeatures) 155 if err != nil { 156 return err 157 } 158 159 for _, fileInfo := range attachments { 160 bytes, err := fileBackend.ReadFile(fileInfo.Path) 161 if err != nil { 162 return err 163 } 164 165 m.Attach(fileInfo.Name, gomail.SetCopyFunc(func(writer io.Writer) error { 166 if _, err := writer.Write(bytes); err != nil { 167 return model.NewAppError("SendMail", "utils.mail.sendMail.attachments.write_error", nil, err.Error(), http.StatusInternalServerError) 168 } 169 return nil 170 })) 171 } 172 } 173 174 conn, err1 := connectToSMTPServer(config) 175 if err1 != nil { 176 return err1 177 } 178 defer conn.Close() 179 180 c, err2 := newSMTPClient(conn, config) 181 if err2 != nil { 182 return err2 183 } 184 defer c.Quit() 185 defer c.Close() 186 187 if err := c.Mail(from.Address); err != nil { 188 return model.NewAppError("SendMail", "utils.mail.send_mail.from_address.app_error", nil, err.Error(), http.StatusInternalServerError) 189 } 190 191 if err := c.Rcpt(smtpTo); err != nil { 192 return model.NewAppError("SendMail", "utils.mail.send_mail.to_address.app_error", nil, err.Error(), http.StatusInternalServerError) 193 } 194 195 w, err := c.Data() 196 if err != nil { 197 return model.NewAppError("SendMail", "utils.mail.send_mail.msg_data.app_error", nil, err.Error(), http.StatusInternalServerError) 198 } 199 200 _, err = m.WriteTo(w) 201 if err != nil { 202 return model.NewAppError("SendMail", "utils.mail.send_mail.msg.app_error", nil, err.Error(), http.StatusInternalServerError) 203 } 204 err = w.Close() 205 if err != nil { 206 return model.NewAppError("SendMail", "utils.mail.send_mail.close.app_error", nil, err.Error(), http.StatusInternalServerError) 207 } 208 209 return nil 210 }