eintopf.info@v0.13.16/service/email/email.go (about)

     1  // Copyright (C) 2023 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package email
    17  
    18  import (
    19  	"crypto/tls"
    20  	"fmt"
    21  	"net"
    22  	"net/mail"
    23  	"net/smtp"
    24  
    25  	"golang.org/x/exp/slices"
    26  )
    27  
    28  // Config  is used to configure the email service.
    29  type Config struct {
    30  	SMTPHost     string
    31  	SMTPPassword string
    32  	Mail         string
    33  	DisableTLS   bool
    34  	AllowList    []string
    35  }
    36  
    37  type Service struct {
    38  	config Config
    39  }
    40  
    41  func NewService(config Config) *Service {
    42  	return &Service{config: config}
    43  }
    44  
    45  var ErrDisallowedEmail = fmt.Errorf("email is not in allow list")
    46  
    47  func (s *Service) Send(email, subject, body string) error {
    48  	if len(s.config.AllowList) > 0 && !slices.Contains(s.config.AllowList, email) {
    49  		return ErrDisallowedEmail
    50  	}
    51  	from := mail.Address{Address: s.config.Mail}
    52  	to := mail.Address{Address: email}
    53  
    54  	// Setup headers
    55  	headers := map[string]string{
    56  		"From":         from.String(),
    57  		"To":           to.String(),
    58  		"MIME-Version": "1.0",
    59  		"Content-Type": "text/text; charset=utf-8",
    60  		"Subject":      subject,
    61  	}
    62  
    63  	// Setup message
    64  	message := ""
    65  	for k, v := range headers {
    66  		message += fmt.Sprintf("%s: %s\r\n", k, v)
    67  	}
    68  	message += "\r\n" + body
    69  
    70  	c, err := smtp.Dial(s.config.SMTPHost)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// TLS
    76  	host, _, _ := net.SplitHostPort(s.config.SMTPHost)
    77  	if !s.config.DisableTLS {
    78  		tlsconfig := &tls.Config{InsecureSkipVerify: true, ServerName: host}
    79  		if err := c.StartTLS(tlsconfig); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	// Auth
    85  	if s.config.SMTPPassword != "" {
    86  		auth := smtp.PlainAuth("", s.config.Mail, s.config.SMTPPassword, host)
    87  		if err := c.Auth(auth); err != nil {
    88  			return err
    89  		}
    90  	}
    91  
    92  	// To && From
    93  	if err := c.Mail(from.Address); err != nil {
    94  		return err
    95  	}
    96  	if err := c.Rcpt(to.Address); err != nil {
    97  		return err
    98  	}
    99  
   100  	// Data
   101  	w, err := c.Data()
   102  	if err != nil {
   103  		return err
   104  	}
   105  	if _, err := w.Write([]byte(message)); err != nil {
   106  		return err
   107  	}
   108  	if err := w.Close(); err != nil {
   109  		return err
   110  	}
   111  
   112  	return c.Quit()
   113  }