func dial(host string, port string, encryption Encryption, config *tls.Config) (*smtpClient, error) {
	var conn net.Conn
	var err error
	address := host + ":" + port
	switch encryption {
	case EncryptionSSL, EncryptionSSLTLS:
		conn, err = tls.Dial("tcp", address, config)
	default:
		conn, err = net.Dial("tcp", address)
	}
	if err != nil {
		return nil, errors.New("Mail Error on dialing with encryption type " + encryption.String() + ": " + err.Error())
	}
	c, err := newClient(conn, host)
	if err != nil {
		return nil, fmt.Errorf("Mail Error on smtp dial: %w", err)
	}
	return c, err
}
dial connects to the smtp server with the request encryption type
package mail
import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/mail"
	"net/textproto"
	"strconv"
	"time"
)
type Email struct {
	from        string
	sender      string
	replyTo     string
	returnPath  string
	recipients  []string
	headers     textproto.MIMEHeader
	parts       []part
	attachments []*File
	inlines     []*File
	Charset     string
	Encoding    encoding
	Error       error
	SMTPServer  *smtpClient
}
type SMTPServer struct {
	Authentication AuthType
	Encryption     Encryption
	Username       string
	Password       string
	Helo           string
	ConnectTimeout time.Duration
	SendTimeout    time.Duration
	Host           string
	Port           int
	KeepAlive      bool
	TLSConfig      *tls.Config
}
type SMTPClient struct {
	Client      *smtpClient
	KeepAlive   bool
	SendTimeout time.Duration
}
type part struct {
	contentType string
	body        *bytes.Buffer
}
type Encryption int
const (
	EncryptionNone Encryption = iota
	EncryptionSSL
	EncryptionTLS
	EncryptionSSLTLS
	EncryptionSTARTTLS
)
var encryptionTypes = [...]string{"None", "SSL/TLS", "STARTTLS", "SSL/TLS", "STARTTLS"}
func (encryption Encryption) String() string {
	return encryptionTypes[encryption]
}
type encoding int
const (
	EncodingNone encoding = iota
	EncodingBase64
	EncodingQuotedPrintable
)
var encodingTypes = [...]string{"binary", "base64", "quoted-printable"}
func (encoding encoding) string() string {
	return encodingTypes[encoding]
}
type contentType int
const (
	TextPlain contentType = iota
	TextHTML
	TextCalendar
)
var contentTypes = [...]string{"text/plain", "text/html", "text/calendar"}
func (contentType contentType) string() string {
	return contentTypes[contentType]
}
type AuthType int
const (
	AuthPlain AuthType = iota
	AuthLogin
	AuthCRAMMD5
	AuthNone
)
func NewMSG() *Email {
	email := &Email{
		headers:  make(textproto.MIMEHeader),
		Charset:  "UTF-8",
		Encoding: EncodingQuotedPrintable,
	}
	email.AddHeader("MIME-Version", "1.0")
	return email
}
func NewSMTPClient() *SMTPServer {
	server := &SMTPServer{
		Authentication: AuthPlain,
		Encryption:     EncryptionNone,
		ConnectTimeout: 10 * time.Second,
		SendTimeout:    10 * time.Second,
		Helo:           "localhost",
	}
	return server
}
func (server *SMTPServer) GetEncryptionType() Encryption {
	return server.Encryption
}
func (email *Email) GetError() error {
	return email.Error
}
func (email *Email) SetFrom(address string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("From", address)
	return email
}
func (email *Email) SetSender(address string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("Sender", address)
	return email
}
func (email *Email) SetReplyTo(address string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("Reply-To", address)
	return email
}
func (email *Email) SetReturnPath(address string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("Return-Path", address)
	return email
}
func (email *Email) AddTo(addresses ...string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("To", addresses...)
	return email
}
func (email *Email) AddCc(addresses ...string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("Cc", addresses...)
	return email
}
func (email *Email) AddBcc(addresses ...string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddAddresses("Bcc", addresses...)
	return email
}
func (email *Email) AddAddresses(header string, addresses ...string) *Email {
	if email.Error != nil {
		return email
	}
	found := false
	for _, h := range []string{"To", "Cc", "Bcc", "From", "Sender", "Reply-To", "Return-Path"} {
		if header == h {
			found = true
		}
	}
	if !found {
		email.Error = errors.New("Mail Error: Invalid address header; Header: [" + header + "]")
		return email
	}
	for i := range addresses {
		var address = new(mail.Address)
		var err error
		if len(addresses[i]) > 0 {
			address, err = mail.ParseAddress(addresses[i])
			if err != nil {
				email.Error = errors.New("Mail Error: " + err.Error() + "; Header: [" + header + "] Address: [" + addresses[i] + "]")
				return email
			}
		} else {
			continue
		}
		switch {
		case header == "Sender" && len(email.sender) > 0:
			fallthrough
		case header == "Reply-To" && len(email.replyTo) > 0:
			fallthrough
		case header == "Return-Path" && len(email.returnPath) > 0:
			email.Error = errors.New("Mail Error: There can only be one \"" + header + "\" address; Header: [" + header + "] Address: [" + addresses[i] + "]")
			return email
		default:
		}
		switch header {
		case "From":
			if len(email.from) > 0 && header == "From" {
				email.headers.Del("From")
			}
			email.from = address.Address
		case "Sender":
			email.sender = address.Address
		case "Reply-To":
			email.replyTo = address.Address
		case "Return-Path":
			email.returnPath = address.Address
		default:
			email.recipients, err = addAddress(email.recipients, address.Address)
			if err != nil {
				email.Error = errors.New("Mail Error: " + err.Error() + "; Header: [" + header + "] Address: [" + addresses[i] + "]")
				return email
			}
		}
		if email.from != "" && email.sender != "" && email.from == email.sender {
			email.sender = ""
			email.headers.Del("Sender")
			email.Error = errors.New("Mail Error: From and Sender should not be set to the same address")
			return email
		}
		if header != "Bcc" && header != "Return-Path" {
			email.headers.Add(header, address.String())
		}
	}
	return email
}
func addAddress(addressList []string, address string) ([]string, error) {
	for _, a := range addressList {
		if address == a {
			return addressList, errors.New("Mail Error: Address: [" + address + "] has already been added")
		}
	}
	return append(addressList, address), nil
}
type priority int
const (
	PriorityLow priority = iota
	PriorityHigh
)
func (email *Email) SetPriority(priority priority) *Email {
	if email.Error != nil {
		return email
	}
	switch priority {
	case PriorityLow:
		email.AddHeaders(textproto.MIMEHeader{
			"X-Priority":        {"5 (Lowest)"},
			"X-MSMail-Priority": {"Low"},
			"Importance":        {"Low"},
		})
	case PriorityHigh:
		email.AddHeaders(textproto.MIMEHeader{
			"X-Priority":        {"1 (Highest)"},
			"X-MSMail-Priority": {"High"},
			"Importance":        {"High"},
		})
	default:
	}
	return email
}
func (email *Email) SetDate(dateTime string) *Email {
	if email.Error != nil {
		return email
	}
	const dateFormat = "2006-01-02 15:04:05 MST"
	dt, err := time.Parse(dateFormat, dateTime)
	if err != nil {
		email.Error = errors.New("Mail Error: Setting date failed with: " + err.Error())
		return email
	}
	email.headers.Set("Date", dt.Format(time.RFC1123Z))
	return email
}
func (email *Email) SetSubject(subject string) *Email {
	if email.Error != nil {
		return email
	}
	email.AddHeader("Subject", subject)
	return email
}
func (email *Email) SetBody(contentType contentType, body string) *Email {
	if email.Error != nil {
		return email
	}
	email.parts = []part{
		{
			contentType: contentType.string(),
			body:        bytes.NewBufferString(body),
		},
	}
	return email
}
func (email *Email) SetBodyData(contentType contentType, body []byte) *Email {
	if email.Error != nil {
		return email
	}
	email.parts = []part{
		{
			contentType: contentType.string(),
			body:        bytes.NewBuffer(body),
		},
	}
	return email
}
func (email *Email) AddHeader(header string, values ...string) *Email {
	if email.Error != nil {
		return email
	}
	if len(values) < 1 {
		email.Error = errors.New("Mail Error: no value provided; Header: [" + header + "]")
		return email
	}
	header = textproto.CanonicalMIMEHeaderKey(header)
	switch header {
	case "Sender":
		fallthrough
	case "From":
		fallthrough
	case "To":
		fallthrough
	case "Bcc":
		fallthrough
	case "Cc":
		fallthrough
	case "Reply-To":
		fallthrough
	case "Return-Path":
		email.AddAddresses(header, values...)
	case "Date":
		if len(values) > 1 {
			email.Error = errors.New("Mail Error: To many dates provided")
			return email
		}
		email.SetDate(values[0])
	default:
		email.headers[header] = values
	}
	return email
}
func (email *Email) AddHeaders(headers textproto.MIMEHeader) *Email {
	if email.Error != nil {
		return email
	}
	for header, values := range headers {
		email.AddHeader(header, values...)
	}
	return email
}
func (email *Email) AddAlternative(contentType contentType, body string) *Email {
	if email.Error != nil {
		return email
	}
	email.parts = append(email.parts,
		part{
			contentType: contentType.string(),
			body:        bytes.NewBufferString(body),
		},
	)
	return email
}
func (email *Email) AddAlternativeData(contentType contentType, body []byte) *Email {
	if email.Error != nil {
		return email
	}
	email.parts = append(email.parts,
		part{
			contentType: contentType.string(),
			body:        bytes.NewBuffer(body),
		},
	)
	return email
}
func (email *Email) GetFrom() string {
	from := email.returnPath
	if from == "" {
		from = email.sender
		if from == "" {
			from = email.from
			if from == "" {
				from = email.replyTo
			}
		}
	}
	return from
}
func (email *Email) GetRecipients() []string {
	return email.recipients
}
func (email *Email) hasMixedPart() bool {
	return (len(email.parts) > 0 && len(email.attachments) > 0) || len(email.attachments) > 1
}
func (email *Email) hasRelatedPart() bool {
	return (len(email.parts) > 0 && len(email.inlines) > 0) || len(email.inlines) > 1
}
func (email *Email) hasAlternativePart() bool {
	return len(email.parts) > 1
}
func (email *Email) GetMessage() string {
	msg := newMessage(email)
	if email.hasMixedPart() {
		msg.openMultipart("mixed")
	}
	if email.hasRelatedPart() {
		msg.openMultipart("related")
	}
	if email.hasAlternativePart() {
		msg.openMultipart("alternative")
	}
	for _, part := range email.parts {
		msg.addBody(part.contentType, part.body.Bytes())
	}
	if email.hasAlternativePart() {
		msg.closeMultipart()
	}
	msg.addFiles(email.inlines, true)
	if email.hasRelatedPart() {
		msg.closeMultipart()
	}
	msg.addFiles(email.attachments, false)
	if email.hasMixedPart() {
		msg.closeMultipart()
	}
	return msg.getHeaders() + msg.body.String()
}
func (email *Email) Send(client *SMTPClient) error {
	return email.SendEnvelopeFrom(email.from, client)
}
func (email *Email) SendEnvelopeFrom(from string, client *SMTPClient) error {
	if email.Error != nil {
		return email.Error
	}
	if from == "" {
		from = email.from
	}
	if len(email.recipients) < 1 {
		return errors.New("Mail Error: No recipient specified")
	}
	msg := email.GetMessage()
	return send(from, email.recipients, msg, client)
}