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  }