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  }