github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/notify/mailer.go (about) 1 /** 2 * mailer.go 3 * 4 * Created on: October 23 2013 5 * Author: Valeri Karpov <valeri.karpov@mongodb.com> 6 * 7 * Defines a functional abstraction for sending an email and a few concrete 8 * functions matching that abstraction. 9 * 10 */ 11 12 package notify 13 14 import ( 15 "bytes" 16 "crypto/tls" 17 "encoding/base64" 18 "fmt" 19 "net/mail" 20 "net/smtp" 21 "strings" 22 23 "github.com/mongodb/grip" 24 "github.com/pkg/errors" 25 ) 26 27 type Mailer interface { 28 SendMail([]string, string, string) error 29 } 30 31 type SmtpMailer struct { 32 From string 33 Server string 34 Port int 35 UseSSL bool 36 Username string 37 Password string 38 } 39 40 /* Connects an SMTP server (usually localhost:25 in prod) and uses that to 41 send an email with the body encoded in base64. */ 42 func (self SmtpMailer) SendMail(recipients []string, subject, body string) error { 43 // 'recipients' is expected a comma-separated list of emails in either of 44 // these formats: 45 // - bob@example.com 46 // - Bob Smith <bob@example.com> 47 var c *smtp.Client 48 var err error 49 50 if self.UseSSL { 51 var tlsCon *tls.Conn 52 53 tlsCon, err = tls.Dial("tcp", fmt.Sprintf("%v:%v", self.Server, self.Port), &tls.Config{}) 54 if err != nil { 55 return err 56 } 57 c, err = smtp.NewClient(tlsCon, self.Server) 58 } else { 59 c, err = smtp.Dial(fmt.Sprintf("%v:%v", self.Server, self.Port)) 60 } 61 62 if err != nil { 63 return err 64 } 65 66 if self.Username != "" { 67 err = c.Auth(smtp.PlainAuth("", self.Username, self.Password, self.Server)) 68 if err != nil { 69 return err 70 } 71 } 72 73 // Set the sender 74 from := mail.Address{ 75 Name: "Evergreen Alerts", 76 Address: self.From, 77 } 78 79 if err = c.Mail(self.From); err != nil { 80 grip.Errorf("Error establishing mail sender (%s): %+v", self.From, err) 81 return err 82 } 83 84 // Set the recipient 85 for _, recipient := range recipients { 86 err = c.Rcpt(recipient) 87 if err != nil { 88 grip.Errorf("Error establishing mail recipient (%s): %+v", recipient, err) 89 return err 90 } 91 } 92 93 // Send the email body. 94 wc, err := c.Data() 95 if err != nil { 96 return err 97 } 98 defer wc.Close() 99 100 // set header information 101 header := make(map[string]string) 102 header["From"] = from.String() 103 header["To"] = strings.Join(recipients, ", ") 104 header["Subject"] = encodeRFC2047(subject) 105 header["MIME-Version"] = "1.0" 106 header["Content-Type"] = "text/html; charset=\"utf-8\"" 107 header["Content-Transfer-Encoding"] = "base64" 108 109 message := "" 110 for k, v := range header { 111 message += fmt.Sprintf("%s: %s\r\n", k, v) 112 } 113 114 message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body)) 115 116 // write the body 117 buf := bytes.NewBufferString(message) 118 _, err = buf.WriteTo(wc) 119 120 return errors.WithStack(err) 121 }