email.go 2.98 KB
package webutility

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

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

type Email struct {
	To      []string
	From    string
	Subject string
	Body    string

	config *EmailConfig
}

func NewEmail() *Email {
	return &Email{
		To:      nil,
		From:    "",
		Subject: "",
		Body:    "",
		config:  nil,
	}
}

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

func (e *Email) SetFrom(from string) {
	e.From = from
}

func (e *Email) SetTo(to []string) {
	e.To = to
}

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

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

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

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

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

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

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

	return str.String()
}

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

func (email *Email) Send() error {
	if email.config == nil {
		return errors.New("email configuration not provided")
	}
	conf := email.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(email.From); err != nil {
		return err
	}

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

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

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

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

	return c.Quit()
}

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"`
}

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,
	}
}