email.go 3.11 KB
package webutility

// TODO(markO): test test test test test (and open source?)

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net/smtp"
	"strings"
)

// Email ...
type Email struct {
	recipients []string
	sender     string
	subject    string
	body       string

	config *EmailConfig
}

// NewEmail ...
func NewEmail() *Email {
	return new(Email)
}

// EmailConfig ...
type EmailConfig struct {
	ServerName string `json:"-"`
	Identity   string `json:"-"`
	Username   string `json:"username"`
	Password   string `json:"password"`
	Host       string `json:"host"`
	Port       int    `json:"port"`
}

// NewEmailConfig ...
func NewEmailConfig(ident, uname, pword, host string, port int) *EmailConfig {
	return &EmailConfig{
		ServerName: host + fmt.Sprintf(":%d", port),
		Identity:   ident,
		Username:   uname,
		Password:   pword,
		Host:       host,
		Port:       port,
	}
}

// Config ...
func (e *Email) Config(cfg *EmailConfig) {
	e.config = cfg
}

// Sender ...
func (e *Email) Sender(from string) {
	e.sender = from
}

// AddRecipient ...
func (e *Email) AddRecipient(r string) {
	e.recipients = append(e.recipients, r)
}

// Subject ...
func (e *Email) Subject(sub string) {
	e.subject = sub
}

func (e *Email) Write(body string) {
	e.body = body
}

func (e *Email) String() string {
	var str strings.Builder

	str.WriteString("From:" + e.sender + "\r\n")

	str.WriteString("To:")
	for i := range e.recipients {
		if i > 0 {
			str.WriteString(",")
		}
		str.WriteString(e.recipients[i])
	}
	str.WriteString("\r\n")

	str.WriteString("Subject:" + e.subject + "\r\n")

	// body
	str.WriteString("\r\n" + e.body + "\r\n")

	return str.String()
}

// Bytes ...
func (e *Email) Bytes() []byte {
	return []byte(e.String())
}

// Send ...
func (e *Email) Send() error {
	if e.config == nil {
		return errors.New("email configuration not provided")
	}
	conf := e.config

	c, err := smtp.Dial(conf.ServerName)
	if err != nil {
		return err
	}
	defer c.Close()

	/*
		// not sure if this is needed
		if err = c.Hello(conf.ServerName); err != nil {
			return err
		}
	*/

	if ok, _ := c.Extension("STARTTLS"); ok {
		// disable stupid tls check for self-signed certificates
		config := &tls.Config{
			ServerName:         conf.ServerName,
			InsecureSkipVerify: true,
		}
		/*
			// for golang testing
			if testHookStartTLS != nil {
				testHookStartTLS(config)
			}
		*/
		if err = c.StartTLS(config); err != nil {
			return err
		}
	}

	/*
		// don't know what to do with this
		if a != nil && c.ext != nil {
			if _, ok := c.ext["AUTH"]; !ok {
				return errors.New("smtp: server doesn't support AUTH")
			}
			if err = c.Auth(a); err != nil {
				return err
			}
		}
	*/

	// Set up authentication information.
	auth := smtp.PlainAuth(conf.Identity, conf.Username, conf.Password, conf.Host)
	if err = c.Auth(auth); err != nil {
		return err
	}

	if err = c.Mail(e.sender); err != nil {
		return err
	}

	for _, addr := range e.recipients {
		if err = c.Rcpt(addr); err != nil {
			return err
		}
	}

	w, err := c.Data()
	if err != nil {
		return err
	}

	_, err = w.Write(e.Bytes())
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	return c.Quit()
}