diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index ba493d0..f982bb6 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,12 +4,12 @@
-
+
-
-
+
+
@@ -97,7 +97,8 @@
-
+
+
true
diff --git a/cmd/main.go b/cmd/main.go
index 3ab1202..897ae16 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -21,10 +21,14 @@ func main() {
fmt.Printf("failed to set FROM addres: %s", err)
os.Exit(1)
}
- m.ToIgnoreInvalid("test@test.de", "foo@bar.de", "blubb@blah.com")
+ if err := m.To("t1+2941@test.de", "foo@bar.de", "blubb@blah.com"); err != nil {
+ fmt.Printf("failed to set TO address: %s", err)
+ os.Exit(1)
+ }
m.CcIgnoreInvalid("cc@test.de", "bar.de", "cc@blah.com")
m.BccIgnoreInvalid("bcc@test.de", "bcc@blah.com")
- m.SetHeader("Foo", "bar")
+ m.Subject("This is the Subject with Umlauts: üöäß")
+ m.SetHeader(mail.HeaderContentLang, "en")
m.SetMessageID()
m.SetDate()
m.SetBulk()
diff --git a/encoding.go b/encoding.go
index c82d37e..44e333b 100644
--- a/encoding.go
+++ b/encoding.go
@@ -1,8 +1,12 @@
package mail
-// Encoding represents a MIME encoding scheme like quoted-printable or base64.
+// Encoding represents a MIME encoding scheme like quoted-printable or Base64.
type Encoding string
+// Charset represents a character set for the encodingA
+type Charset string
+
+// List of supported encodings
const (
// EncodingB64 represents the Base64 encoding as specified in RFC 2045.
EncodingB64 Encoding = "base64"
@@ -13,3 +17,102 @@ const (
// NoEncoding avoids any character encoding (except of the mail headers)
NoEncoding Encoding = "8bit"
)
+
+// List of common charsets
+const (
+ // CharsetUTF7 represents the "UTF-7" charset
+ CharsetUTF7 Charset = "UTF-7"
+
+ // CharsetUTF8 represents the "UTF-8" charset
+ CharsetUTF8 Charset = "UTF-8"
+
+ // CharsetASCII represents the "US-ASCII" charset
+ CharsetASCII Charset = "US-ASCII"
+
+ // CharsetISO88591 represents the "ISO-8859-1" charset
+ CharsetISO88591 Charset = "ISO-8859-1"
+
+ // CharsetISO88592 represents the "ISO-8859-2" charset
+ CharsetISO88592 Charset = "ISO-8859-2"
+
+ // CharsetISO88593 represents the "ISO-8859-3" charset
+ CharsetISO88593 Charset = "ISO-8859-3"
+
+ // CharsetISO88594 represents the "ISO-8859-4" charset
+ CharsetISO88594 Charset = "ISO-8859-4"
+
+ // CharsetISO88595 represents the "ISO-8859-5" charset
+ CharsetISO88595 Charset = "ISO-8859-5"
+
+ // CharsetISO88596 represents the "ISO-8859-6" charset
+ CharsetISO88596 Charset = "ISO-8859-6"
+
+ // CharsetISO88597 represents the "ISO-8859-7" charset
+ CharsetISO88597 Charset = "ISO-8859-7"
+
+ // CharsetISO88599 represents the "ISO-8859-9" charset
+ CharsetISO88599 Charset = "ISO-8859-9"
+
+ // CharsetISO885913 represents the "ISO-8859-13" charset
+ CharsetISO885913 Charset = "ISO-8859-13"
+
+ // CharsetISO885914 represents the "ISO-8859-14" charset
+ CharsetISO885914 Charset = "ISO-8859-14"
+
+ // CharsetISO885915 represents the "ISO-8859-15" charset
+ CharsetISO885915 Charset = "ISO-8859-15"
+
+ // CharsetISO885916 represents the "ISO-8859-16" charset
+ CharsetISO885916 Charset = "ISO-8859-16"
+
+ // CharsetISO2022JP represents the "ISO-2022-JP" charset
+ CharsetISO2022JP Charset = "ISO-2022-JP"
+
+ // CharsetISO2022KR represents the "ISO-2022-KR" charset
+ CharsetISO2022KR Charset = "ISO-2022-KR"
+
+ // CharsetWindows1250 represents the "windows-1250" charset
+ CharsetWindows1250 Charset = "windows-1250"
+
+ // CharsetWindows1251 represents the "windows-1251" charset
+ CharsetWindows1251 Charset = "windows-1251"
+
+ // CharsetWindows1252 represents the "windows-1252" charset
+ CharsetWindows1252 Charset = "windows-1252"
+
+ // CharsetWindows1255 represents the "windows-1255" charset
+ CharsetWindows1255 Charset = "windows-1255"
+
+ // CharsetWindows1256 represents the "windows-1256" charset
+ CharsetWindows1256 Charset = "windows-1256"
+
+ // CharsetKOI8R represents the "KOI8-R" charset
+ CharsetKOI8R Charset = "KOI8-R"
+
+ // CharsetKOI8U represents the "KOI8-U" charset
+ CharsetKOI8U Charset = "KOI8-U"
+
+ // CharsetBig5 represents the "Big5" charset
+ CharsetBig5 Charset = "Big5"
+
+ // CharsetGB18030 represents the "GB18030" charset
+ CharsetGB18030 Charset = "GB18030"
+
+ // CharsetGB2312 represents the "GB2312" charset
+ CharsetGB2312 Charset = "GB2312"
+
+ // CharsetTIS620 represents the "TIS-620" charset
+ CharsetTIS620 Charset = "TIS-620"
+
+ // CharsetEUCKR represents the "EUC-KR" charset
+ CharsetEUCKR Charset = "EUC-KR"
+
+ // CharsetShiftJIS represents the "Shift_JIS" charset
+ CharsetShiftJIS Charset = "Shift_JIS"
+
+ // CharsetUnknown represents the "Unknown" charset
+ CharsetUnknown Charset = "Unknown"
+
+ // CharsetGBK represents the "GBK" charset
+ CharsetGBK Charset = "GBK"
+)
diff --git a/header.go b/header.go
index a642e5a..af8d300 100644
--- a/header.go
+++ b/header.go
@@ -18,6 +18,15 @@ const (
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
HeaderDate Header = "Date"
+ // HeaderInReplyTo represents the "In-Reply-To" field
+ HeaderInReplyTo Header = "In-Reply-To"
+
+ // HeaderListUnsubscribe is the "List-Unsubscribe" header field
+ HeaderListUnsubscribe Header = "List-Unsubscribe"
+
+ // HeaderListUnsubscribePost is the "List-Unsubscribe-Post" header field
+ HeaderListUnsubscribePost Header = "List-Unsubscribe-Post"
+
// HeaderMessageID represents the "Message-ID" field for message identification
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
HeaderMessageID Header = "Message-ID"
@@ -26,24 +35,27 @@ const (
// See: https://datatracker.ietf.org/doc/html/rfc2045#section-4
HeaderMIMEVersion Header = "MIME-Version"
- // HeaderPrecedence is the "Precedence" genHeader field
+ // HeaderPrecedence is the "Precedence" header field
HeaderPrecedence Header = "Precedence"
- // HeaderSubject is the "Subject" genHeader field
+ // HeaderReplyTo is the "Reply-To" header field
+ HeaderReplyTo Header = "Reply-To"
+
+ // HeaderSubject is the "Subject" header field
HeaderSubject Header = "Subject"
)
// List of common generic header field names
const (
- // HeaderBcc is the "Blind Carbon Copy" genHeader field
+ // HeaderBcc is the "Blind Carbon Copy" header field
HeaderBcc AddrHeader = "Bcc"
- // HeaderCc is the "Carbon Copy" genHeader field
+ // HeaderCc is the "Carbon Copy" header field
HeaderCc AddrHeader = "Cc"
- // HeaderFrom is the "From" genHeader field
+ // HeaderFrom is the "From" header field
HeaderFrom AddrHeader = "From"
- // HeaderTo is the "Receipient" genHeader field
+ // HeaderTo is the "Receipient" header field
HeaderTo AddrHeader = "To"
)
diff --git a/mailmsg.go b/mailmsg.go
index 19b7009..675f193 100644
--- a/mailmsg.go
+++ b/mailmsg.go
@@ -3,6 +3,7 @@ package mail
import (
"fmt"
"math/rand"
+ "mime"
"net/mail"
"os"
"time"
@@ -11,10 +12,13 @@ import (
// Msg is the mail message struct
type Msg struct {
// charset represents the charset of the mail (defaults to UTF-8)
- charset string
+ charset Charset
+
+ // encoding represents the message encoding (the encoder will be a corresponding WordEncoder)
+ encoding Encoding
// encoder represents a mime.WordEncoder from the std lib
- //encoder mime.WordEncoder
+ encoder mime.WordEncoder
// genHeader is a slice of strings that the different generic mail Header fields
genHeader map[Header][]string
@@ -23,18 +27,61 @@ type Msg struct {
addrHeader map[AddrHeader][]*mail.Address
}
+// MsgOption returns a function that can be used for grouping Msg options
+type MsgOption func(*Msg)
+
// NewMsg returns a new Msg pointer
-func NewMsg() *Msg {
+func NewMsg(o ...MsgOption) *Msg {
m := &Msg{
- charset: "UTF-8",
+ encoding: EncodingQP,
+ charset: CharsetUTF8,
genHeader: make(map[Header][]string),
addrHeader: make(map[AddrHeader][]*mail.Address),
}
+
+ // Override defaults with optionally provided MsgOption functions
+ for _, co := range o {
+ if co == nil {
+ continue
+ }
+ co(m)
+ }
+
+ // Set the matcing mime.WordEncoder for the Msg
+ m.setEncoder()
+
return m
}
+// WithCharset overrides the default message charset
+func WithCharset(c Charset) MsgOption {
+ return func(m *Msg) {
+ m.charset = c
+ }
+}
+
+// WithEncoding overrides the default message encoding
+func WithEncoding(e Encoding) MsgOption {
+ return func(m *Msg) {
+ m.encoding = e
+ }
+}
+
+// SetCharset sets the encoding charset of the Msg
+func (m *Msg) SetCharset(c Charset) {
+ m.charset = c
+}
+
+// SetEncoding sets the encoding of the Msg
+func (m *Msg) SetEncoding(e Encoding) {
+ m.encoding = e
+}
+
// SetHeader sets a generic header field of the Msg
func (m *Msg) SetHeader(h Header, v ...string) {
+ for i, hv := range v {
+ v[i] = m.encodeString(hv)
+ }
m.genHeader[h] = v
}
@@ -42,7 +89,7 @@ func (m *Msg) SetHeader(h Header, v ...string) {
func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
var al []*mail.Address
for _, av := range v {
- a, err := mail.ParseAddress(av)
+ a, err := mail.ParseAddress(m.encodeString(av))
if err != nil {
return fmt.Errorf("failed to parse mail address header %q: %w", av, err)
}
@@ -62,7 +109,7 @@ func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
func (m *Msg) SetAddrHeaderIgnoreInvalid(h AddrHeader, v ...string) {
var al []*mail.Address
for _, av := range v {
- a, err := mail.ParseAddress(av)
+ a, err := mail.ParseAddress(m.encodeString(av))
if err != nil {
continue
}
@@ -81,6 +128,11 @@ func (m *Msg) To(t ...string) error {
return m.SetAddrHeader(HeaderTo, t...)
}
+// Subject sets the "Subject" header field of the Msg
+func (m *Msg) Subject(s string) {
+ m.SetHeader(HeaderSubject, s)
+}
+
// ToIgnoreInvalid takes and validates a given mail address list sets the To: addresses of the Msg
// Any provided address that is not RFC5322 compliant, will be ignored
func (m *Msg) ToIgnoreInvalid(t ...string) {
@@ -126,7 +178,7 @@ func (m *Msg) SetMessageID() {
// SetMessageIDWithValue sets the message id for the mail
func (m *Msg) SetMessageIDWithValue(v string) {
- m.SetHeader(HeaderMessageID, v)
+ m.SetHeader(HeaderMessageID, fmt.Sprintf("<%s>", v))
}
// SetBulk sets the "Precedence: bulk" genHeader which is recommended for
@@ -149,3 +201,21 @@ func (m *Msg) Header() {
fmt.Printf("Generic header: %+v\n", m.genHeader)
}
+
+// setEncoder creates a new mime.WordEncoder based on the encoding setting of the message
+func (m *Msg) setEncoder() {
+ switch m.encoding {
+ case EncodingQP:
+ m.encoder = mime.QEncoding
+ case EncodingB64:
+ m.encoder = mime.BEncoding
+ default:
+ m.encoder = mime.QEncoding
+ }
+}
+
+// encodeString encodes a string based on the configured message encoder and the corresponding
+// charset for the Msg
+func (m *Msg) encodeString(s string) string {
+ return m.encoder.Encode(string(m.charset), s)
+}