mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-12 17:02:55 +01:00
Started work on charset and encoding handling
This commit is contained in:
parent
98d7982738
commit
07fdf64cbc
5 changed files with 210 additions and 20 deletions
|
@ -4,12 +4,12 @@
|
||||||
<option name="autoReloadType" value="ALL" />
|
<option name="autoReloadType" value="ALL" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Lots of cleanups and refactoring">
|
<list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Calling it a day...">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/encoding.go" beforeDir="false" afterPath="$PROJECT_DIR$/encoding.go" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/header.go" beforeDir="false" afterPath="$PROJECT_DIR$/header.go" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/header.go" beforeDir="false" afterPath="$PROJECT_DIR$/header.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/mailmsg.go" beforeDir="false" afterPath="$PROJECT_DIR$/mailmsg.go" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
@ -97,7 +97,8 @@
|
||||||
<MESSAGE value="Implemented SMTP AUTH" />
|
<MESSAGE value="Implemented SMTP AUTH" />
|
||||||
<MESSAGE value="Better context and connection handling" />
|
<MESSAGE value="Better context and connection handling" />
|
||||||
<MESSAGE value="Lots of cleanups and refactoring" />
|
<MESSAGE value="Lots of cleanups and refactoring" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Lots of cleanups and refactoring" />
|
<MESSAGE value="Calling it a day..." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Calling it a day..." />
|
||||||
</component>
|
</component>
|
||||||
<component name="VgoProject">
|
<component name="VgoProject">
|
||||||
<integration-enabled>true</integration-enabled>
|
<integration-enabled>true</integration-enabled>
|
||||||
|
|
|
@ -21,10 +21,14 @@ func main() {
|
||||||
fmt.Printf("failed to set FROM addres: %s", err)
|
fmt.Printf("failed to set FROM addres: %s", err)
|
||||||
os.Exit(1)
|
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.CcIgnoreInvalid("cc@test.de", "bar.de", "cc@blah.com")
|
||||||
m.BccIgnoreInvalid("bcc@test.de", "bcc@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.SetMessageID()
|
||||||
m.SetDate()
|
m.SetDate()
|
||||||
m.SetBulk()
|
m.SetBulk()
|
||||||
|
|
105
encoding.go
105
encoding.go
|
@ -1,8 +1,12 @@
|
||||||
package mail
|
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
|
type Encoding string
|
||||||
|
|
||||||
|
// Charset represents a character set for the encodingA
|
||||||
|
type Charset string
|
||||||
|
|
||||||
|
// List of supported encodings
|
||||||
const (
|
const (
|
||||||
// EncodingB64 represents the Base64 encoding as specified in RFC 2045.
|
// EncodingB64 represents the Base64 encoding as specified in RFC 2045.
|
||||||
EncodingB64 Encoding = "base64"
|
EncodingB64 Encoding = "base64"
|
||||||
|
@ -13,3 +17,102 @@ const (
|
||||||
// NoEncoding avoids any character encoding (except of the mail headers)
|
// NoEncoding avoids any character encoding (except of the mail headers)
|
||||||
NoEncoding Encoding = "8bit"
|
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"
|
||||||
|
)
|
||||||
|
|
24
header.go
24
header.go
|
@ -18,6 +18,15 @@ const (
|
||||||
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
||||||
HeaderDate Header = "Date"
|
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
|
// HeaderMessageID represents the "Message-ID" field for message identification
|
||||||
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
|
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
|
||||||
HeaderMessageID Header = "Message-ID"
|
HeaderMessageID Header = "Message-ID"
|
||||||
|
@ -26,24 +35,27 @@ const (
|
||||||
// See: https://datatracker.ietf.org/doc/html/rfc2045#section-4
|
// See: https://datatracker.ietf.org/doc/html/rfc2045#section-4
|
||||||
HeaderMIMEVersion Header = "MIME-Version"
|
HeaderMIMEVersion Header = "MIME-Version"
|
||||||
|
|
||||||
// HeaderPrecedence is the "Precedence" genHeader field
|
// HeaderPrecedence is the "Precedence" header field
|
||||||
HeaderPrecedence Header = "Precedence"
|
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"
|
HeaderSubject Header = "Subject"
|
||||||
)
|
)
|
||||||
|
|
||||||
// List of common generic header field names
|
// List of common generic header field names
|
||||||
const (
|
const (
|
||||||
// HeaderBcc is the "Blind Carbon Copy" genHeader field
|
// HeaderBcc is the "Blind Carbon Copy" header field
|
||||||
HeaderBcc AddrHeader = "Bcc"
|
HeaderBcc AddrHeader = "Bcc"
|
||||||
|
|
||||||
// HeaderCc is the "Carbon Copy" genHeader field
|
// HeaderCc is the "Carbon Copy" header field
|
||||||
HeaderCc AddrHeader = "Cc"
|
HeaderCc AddrHeader = "Cc"
|
||||||
|
|
||||||
// HeaderFrom is the "From" genHeader field
|
// HeaderFrom is the "From" header field
|
||||||
HeaderFrom AddrHeader = "From"
|
HeaderFrom AddrHeader = "From"
|
||||||
|
|
||||||
// HeaderTo is the "Receipient" genHeader field
|
// HeaderTo is the "Receipient" header field
|
||||||
HeaderTo AddrHeader = "To"
|
HeaderTo AddrHeader = "To"
|
||||||
)
|
)
|
||||||
|
|
84
mailmsg.go
84
mailmsg.go
|
@ -3,6 +3,7 @@ package mail
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"mime"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,10 +12,13 @@ import (
|
||||||
// Msg is the mail message struct
|
// Msg is the mail message struct
|
||||||
type Msg struct {
|
type Msg struct {
|
||||||
// charset represents the charset of the mail (defaults to UTF-8)
|
// 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 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 is a slice of strings that the different generic mail Header fields
|
||||||
genHeader map[Header][]string
|
genHeader map[Header][]string
|
||||||
|
@ -23,18 +27,61 @@ type Msg struct {
|
||||||
addrHeader map[AddrHeader][]*mail.Address
|
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
|
// NewMsg returns a new Msg pointer
|
||||||
func NewMsg() *Msg {
|
func NewMsg(o ...MsgOption) *Msg {
|
||||||
m := &Msg{
|
m := &Msg{
|
||||||
charset: "UTF-8",
|
encoding: EncodingQP,
|
||||||
|
charset: CharsetUTF8,
|
||||||
genHeader: make(map[Header][]string),
|
genHeader: make(map[Header][]string),
|
||||||
addrHeader: make(map[AddrHeader][]*mail.Address),
|
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
|
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
|
// SetHeader sets a generic header field of the Msg
|
||||||
func (m *Msg) SetHeader(h Header, v ...string) {
|
func (m *Msg) SetHeader(h Header, v ...string) {
|
||||||
|
for i, hv := range v {
|
||||||
|
v[i] = m.encodeString(hv)
|
||||||
|
}
|
||||||
m.genHeader[h] = v
|
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 {
|
func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
|
||||||
var al []*mail.Address
|
var al []*mail.Address
|
||||||
for _, av := range v {
|
for _, av := range v {
|
||||||
a, err := mail.ParseAddress(av)
|
a, err := mail.ParseAddress(m.encodeString(av))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse mail address header %q: %w", av, err)
|
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) {
|
func (m *Msg) SetAddrHeaderIgnoreInvalid(h AddrHeader, v ...string) {
|
||||||
var al []*mail.Address
|
var al []*mail.Address
|
||||||
for _, av := range v {
|
for _, av := range v {
|
||||||
a, err := mail.ParseAddress(av)
|
a, err := mail.ParseAddress(m.encodeString(av))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -81,6 +128,11 @@ func (m *Msg) To(t ...string) error {
|
||||||
return m.SetAddrHeader(HeaderTo, t...)
|
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
|
// 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
|
// Any provided address that is not RFC5322 compliant, will be ignored
|
||||||
func (m *Msg) ToIgnoreInvalid(t ...string) {
|
func (m *Msg) ToIgnoreInvalid(t ...string) {
|
||||||
|
@ -126,7 +178,7 @@ func (m *Msg) SetMessageID() {
|
||||||
|
|
||||||
// SetMessageIDWithValue sets the message id for the mail
|
// SetMessageIDWithValue sets the message id for the mail
|
||||||
func (m *Msg) SetMessageIDWithValue(v string) {
|
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
|
// 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)
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue