| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 | package gomailimport (	"bytes"	"io"	"os"	"path/filepath"	"time")// Message represents an email.type Message struct {	header      header	parts       []*part	attachments []*file	embedded    []*file	charset     string	encoding    Encoding	hEncoder    mimeEncoder	buf         bytes.Buffer}type header map[string][]stringtype part struct {	contentType string	copier      func(io.Writer) error	encoding    Encoding}// NewMessage creates a new message. It uses UTF-8 and quoted-printable encoding// by default.func NewMessage(settings ...MessageSetting) *Message {	m := &Message{		header:   make(header),		charset:  "UTF-8",		encoding: QuotedPrintable,	}	m.applySettings(settings)	if m.encoding == Base64 {		m.hEncoder = bEncoding	} else {		m.hEncoder = qEncoding	}	return m}// Reset resets the message so it can be reused. The message keeps its previous// settings so it is in the same state that after a call to NewMessage.func (m *Message) Reset() {	for k := range m.header {		delete(m.header, k)	}	m.parts = nil	m.attachments = nil	m.embedded = nil}func (m *Message) applySettings(settings []MessageSetting) {	for _, s := range settings {		s(m)	}}// A MessageSetting can be used as an argument in NewMessage to configure an// email.type MessageSetting func(m *Message)// SetCharset is a message setting to set the charset of the email.func SetCharset(charset string) MessageSetting {	return func(m *Message) {		m.charset = charset	}}// SetEncoding is a message setting to set the encoding of the email.func SetEncoding(enc Encoding) MessageSetting {	return func(m *Message) {		m.encoding = enc	}}// Encoding represents a MIME encoding scheme like quoted-printable or base64.type Encoding stringconst (	// QuotedPrintable represents the quoted-printable encoding as defined in	// RFC 2045.	QuotedPrintable Encoding = "quoted-printable"	// Base64 represents the base64 encoding as defined in RFC 2045.	Base64 Encoding = "base64"	// Unencoded can be used to avoid encoding the body of an email. The headers	// will still be encoded using quoted-printable encoding.	Unencoded Encoding = "8bit")// SetHeader sets a value to the given header field.func (m *Message) SetHeader(field string, value ...string) {	m.encodeHeader(value)	m.header[field] = value}func (m *Message) encodeHeader(values []string) {	for i := range values {		values[i] = m.encodeString(values[i])	}}func (m *Message) encodeString(value string) string {	return m.hEncoder.Encode(m.charset, value)}// SetHeaders sets the message headers.func (m *Message) SetHeaders(h map[string][]string) {	for k, v := range h {		m.SetHeader(k, v...)	}}// SetAddressHeader sets an address to the given header field.func (m *Message) SetAddressHeader(field, address, name string) {	m.header[field] = []string{m.FormatAddress(address, name)}}// FormatAddress formats an address and a name as a valid RFC 5322 address.func (m *Message) FormatAddress(address, name string) string {	if name == "" {		return address	}	enc := m.encodeString(name)	if enc == name {		m.buf.WriteByte('"')		for i := 0; i < len(name); i++ {			b := name[i]			if b == '\\' || b == '"' {				m.buf.WriteByte('\\')			}			m.buf.WriteByte(b)		}		m.buf.WriteByte('"')	} else if hasSpecials(name) {		m.buf.WriteString(bEncoding.Encode(m.charset, name))	} else {		m.buf.WriteString(enc)	}	m.buf.WriteString(" <")	m.buf.WriteString(address)	m.buf.WriteByte('>')	addr := m.buf.String()	m.buf.Reset()	return addr}func hasSpecials(text string) bool {	for i := 0; i < len(text); i++ {		switch c := text[i]; c {		case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '.', '"':			return true		}	}	return false}// SetDateHeader sets a date to the given header field.func (m *Message) SetDateHeader(field string, date time.Time) {	m.header[field] = []string{m.FormatDate(date)}}// FormatDate formats a date as a valid RFC 5322 date.func (m *Message) FormatDate(date time.Time) string {	return date.Format(time.RFC1123Z)}// GetHeader gets a header field.func (m *Message) GetHeader(field string) []string {	return m.header[field]}// SetBody sets the body of the message. It replaces any content previously set// by SetBody, AddAlternative or AddAlternativeWriter.func (m *Message) SetBody(contentType, body string, settings ...PartSetting) {	m.parts = []*part{m.newPart(contentType, newCopier(body), settings)}}// AddAlternative adds an alternative part to the message.//// It is commonly used to send HTML emails that default to the plain text// version for backward compatibility. AddAlternative appends the new part to// the end of the message. So the plain text part should be added before the// HTML part. See http://en.wikipedia.org/wiki/MIME#Alternativefunc (m *Message) AddAlternative(contentType, body string, settings ...PartSetting) {	m.AddAlternativeWriter(contentType, newCopier(body), settings...)}func newCopier(s string) func(io.Writer) error {	return func(w io.Writer) error {		_, err := io.WriteString(w, s)		return err	}}// AddAlternativeWriter adds an alternative part to the message. It can be// useful with the text/template or html/template packages.func (m *Message) AddAlternativeWriter(contentType string, f func(io.Writer) error, settings ...PartSetting) {	m.parts = append(m.parts, m.newPart(contentType, f, settings))}func (m *Message) newPart(contentType string, f func(io.Writer) error, settings []PartSetting) *part {	p := &part{		contentType: contentType,		copier:      f,		encoding:    m.encoding,	}	for _, s := range settings {		s(p)	}	return p}// A PartSetting can be used as an argument in Message.SetBody,// Message.AddAlternative or Message.AddAlternativeWriter to configure the part// added to a message.type PartSetting func(*part)// SetPartEncoding sets the encoding of the part added to the message. By// default, parts use the same encoding than the message.func SetPartEncoding(e Encoding) PartSetting {	return PartSetting(func(p *part) {		p.encoding = e	})}type file struct {	Name     string	Header   map[string][]string	CopyFunc func(w io.Writer) error}func (f *file) setHeader(field, value string) {	f.Header[field] = []string{value}}// A FileSetting can be used as an argument in Message.Attach or Message.Embed.type FileSetting func(*file)// SetHeader is a file setting to set the MIME header of the message part that// contains the file content.//// Mandatory headers are automatically added if they are not set when sending// the email.func SetHeader(h map[string][]string) FileSetting {	return func(f *file) {		for k, v := range h {			f.Header[k] = v		}	}}// Rename is a file setting to set the name of the attachment if the name is// different than the filename on disk.func Rename(name string) FileSetting {	return func(f *file) {		f.Name = name	}}// SetCopyFunc is a file setting to replace the function that runs when the// message is sent. It should copy the content of the file to the io.Writer.//// The default copy function opens the file with the given filename, and copy// its content to the io.Writer.func SetCopyFunc(f func(io.Writer) error) FileSetting {	return func(fi *file) {		fi.CopyFunc = f	}}func (m *Message) appendFile(list []*file, name string, settings []FileSetting) []*file {	f := &file{		Name:   filepath.Base(name),		Header: make(map[string][]string),		CopyFunc: func(w io.Writer) error {			h, err := os.Open(name)			if err != nil {				return err			}			if _, err := io.Copy(w, h); err != nil {				h.Close()				return err			}			return h.Close()		},	}	for _, s := range settings {		s(f)	}	if list == nil {		return []*file{f}	}	return append(list, f)}// Attach attaches the files to the email.func (m *Message) Attach(filename string, settings ...FileSetting) {	m.attachments = m.appendFile(m.attachments, filename, settings)}// Embed embeds the images to the email.func (m *Message) Embed(filename string, settings ...FileSetting) {	m.embedded = m.appendFile(m.embedded, filename, settings)}
 |