diff --git a/utils/mail.go b/utils/mail.go index 1d80a039..10555a0a 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -31,10 +31,13 @@ import ( "path/filepath" "strconv" "strings" + "sync" ) const ( maxLineLength = 76 + + upperhex = "0123456789ABCDEF" ) // Email is the type used for email messages @@ -74,9 +77,6 @@ func NewEMail(config string) *Email { if err != nil { return nil } - if e.From == "" { - e.From = e.Username - } return e } @@ -228,14 +228,21 @@ func (e *Email) Send() error { to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) // Check to make sure there is at least one recipient and one "From" address - if e.From == "" || len(to) == 0 { - return errors.New("Must specify at least one From address and one To address") + if len(to) == 0 { + return errors.New("Must specify at least one To address") } - from, err := mail.ParseAddress(e.From) + + from, err := mail.ParseAddress(e.Username) if err != nil { return err } - e.From = from.String() + + if len(e.From) == 0 { + e.From = e.Username + } + // use mail's RFC 2047 to encode any string + e.Subject = qEncode("utf-8", e.Subject) + raw, err := e.Bytes() if err != nil { return err @@ -342,3 +349,73 @@ func base64Wrap(w io.Writer, b []byte) { w.Write(out) } } + +// Encode returns the encoded-word form of s. If s is ASCII without special +// characters, it is returned unchanged. The provided charset is the IANA +// charset name of s. It is case insensitive. +// RFC 2047 encoded-word +func qEncode(charset, s string) string { + if !needsEncoding(s) { + return s + } + return encodeWord(charset, s) +} + +func needsEncoding(s string) bool { + for _, b := range s { + if (b < ' ' || b > '~') && b != '\t' { + return true + } + } + return false +} + +// encodeWord encodes a string into an encoded-word. +func encodeWord(charset, s string) string { + buf := getBuffer() + + buf.WriteString("=?") + buf.WriteString(charset) + buf.WriteByte('?') + buf.WriteByte('q') + buf.WriteByte('?') + + enc := make([]byte, 3) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == ' ': + buf.WriteByte('_') + case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_': + buf.WriteByte(b) + default: + enc[0] = '=' + enc[1] = upperhex[b>>4] + enc[2] = upperhex[b&0x0f] + buf.Write(enc) + } + } + buf.WriteString("?=") + + es := buf.String() + putBuffer(buf) + return es +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +func getBuffer() *bytes.Buffer { + return bufPool.Get().(*bytes.Buffer) +} + +func putBuffer(buf *bytes.Buffer) { + if buf.Len() > 1024 { + return + } + buf.Reset() + bufPool.Put(buf) +}