github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/core/utils/mails/mails.go (about) 1 package mails 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "mime" 8 "path/filepath" 9 "time" 10 11 "jaytaylor.com/html2text" 12 13 "github.com/benoitkugler/goACVE/logs" 14 "github.com/jordan-wright/email" 15 ) 16 17 const ( 18 SAUVEGARDE_MAILS = "acve@alwaysdata.net" 19 UNSUBSCRIBE = "contact@acve.asso.fr" 20 ) 21 22 // Utilise l'adresse fournie par les crédences 23 var DefaultReplyTo ReplyTo = defaultReplyTo{} 24 25 type PieceJointe struct { 26 Content []byte 27 Filename string 28 } 29 30 func (pj PieceJointe) attach(e *email.Email) error { 31 ty := mime.TypeByExtension(filepath.Ext(pj.Filename)) 32 _, err := e.Attach(bytes.NewReader(pj.Content), pj.Filename, ty) 33 return err 34 } 35 36 type ReplyTo interface { 37 selectReplyTo(credences logs.SMTP) string 38 } 39 40 type defaultReplyTo struct{} 41 42 func (d defaultReplyTo) selectReplyTo(credences logs.SMTP) string { return credences.ReplyTo } 43 44 type CustomReplyTo string 45 46 func (d CustomReplyTo) selectReplyTo(logs.SMTP) string { return string(d) } 47 48 // Pool est une interface pour envoyer plusieurs mails consécutivement 49 type Pool struct { 50 pool *email.Pool 51 52 pjs []PieceJointe 53 creds logs.SMTP 54 } 55 56 func newMail(to []string, subject, htmlBody string, ccs []string, replyTo ReplyTo, creds logs.SMTP, 57 pjs []PieceJointe) (*email.Email, error) { 58 59 e := email.NewEmail() 60 if replyTo != nil { 61 e.ReplyTo = []string{replyTo.selectReplyTo(creds)} 62 } 63 64 e.To = make([]string, len(to)) 65 for index, m := range to { 66 e.To[index] = creds.GetTo(m) 67 if e.To[index] == "" { 68 return nil, errors.New("Adresse mail non fournie !") 69 } 70 } 71 e.Cc = make([]string, len(ccs)) 72 for index, cc := range ccs { 73 e.Cc[index] = creds.GetTo(cc) 74 } 75 e.From = fmt.Sprintf("ACVE <%s>", creds.User) 76 e.Subject = subject 77 e.HTML = []byte(htmlBody) 78 79 e.Bcc = []string{SAUVEGARDE_MAILS} 80 e.Headers.Set("List-Unsubscribe", fmt.Sprintf("<mailto:%s>", UNSUBSCRIBE)) 81 82 text, err := html2text.FromString(htmlBody) 83 if err != nil { 84 return nil, fmt.Errorf("conversion html -> text impossible : %s", err) 85 } 86 e.Text = []byte(text) 87 88 for _, pj := range pjs { // ajout des pièces jointes 89 if err := pj.attach(e); err != nil { 90 return nil, err 91 } 92 } 93 return e, nil 94 } 95 96 type Mailer interface { 97 SendMail(to, subject, htmlBody string, ccs []string, replyTo ReplyTo) (err error) 98 } 99 100 type basicMailer struct { 101 smtp logs.SMTP 102 } 103 104 func NewMailer(smtp logs.SMTP) Mailer { 105 return basicMailer{smtp: smtp} 106 } 107 108 // SendMail envoie un mail directement. 109 func (b basicMailer) SendMail(to, subject, htmlBody string, ccs []string, replyTo ReplyTo) (err error) { 110 e, err := newMail([]string{to}, subject, htmlBody, ccs, replyTo, b.smtp, nil) 111 if err != nil { 112 return err 113 } 114 115 from, auth := b.smtp.GetFromAuth() 116 if err = e.Send(from, auth); err != nil { 117 return fmt.Errorf("Impossible d'envoyer le mail : %s", err) 118 } 119 return nil 120 } 121 122 // NewPool crée une interface pour envoyer plusieurs fois un mail avec 123 // les mêmes pièces jointes. 124 // Utiliser `SendMail` (plusieurs fois) puis `Close` (une fois) 125 func NewPool(credences logs.SMTP, pjs []PieceJointe) (Pool, error) { 126 from, auth := credences.GetFromAuth() 127 p, err := email.NewPool(from, 1, auth) 128 if err != nil { 129 return Pool{}, err 130 } 131 return Pool{pool: p, creds: credences, pjs: pjs}, err 132 } 133 134 func (p Pool) SendMail(to, subject, htmlBody string, ccs []string, replyTo ReplyTo) error { 135 mail, err := newMail([]string{to}, subject, htmlBody, ccs, replyTo, p.creds, p.pjs) 136 if err != nil { 137 return err 138 } 139 if err := p.pool.Send(mail, 10*time.Second); err != nil { 140 return fmt.Errorf("Impossible d'envoyer le mail : %s", err) 141 } 142 return nil 143 } 144 145 func (p Pool) Close() { 146 p.pool.Close() 147 }