Skip to content

Commit

Permalink
Add option to have no header encoding (#104)
Browse files Browse the repository at this point in the history
* Thread through HeaderEncoding value

* Use separate enum for header encodings

This is necessary as we support a separate set of encodings for headers

* Add support for HeaderEncodingNone

* Add tests for None encoding for headers
  • Loading branch information
meain committed Mar 12, 2024
1 parent 500bfa5 commit 3161d31
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 154 deletions.
53 changes: 36 additions & 17 deletions email.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@ import (

// Email represents an email message.
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
DkimMsg string
from string
sender string
replyTo string
returnPath string
recipients []string
headers textproto.MIMEHeader
parts []part
attachments []*File
inlines []*File
Charset string
Encoding encoding
HeaderEncoding headerEncoding
Error error
SMTPServer *smtpClient
DkimMsg string

// UseProvidedAddress if set to true will disable any parsing and
// validation of addresses and uses the address provided by the user
Expand Down Expand Up @@ -110,6 +111,23 @@ func (encryption Encryption) String() string {
return encryptionTypes[encryption]
}

type headerEncoding int

const (
// HeaderEncodingNone turns off encoding on the message headers
// https://www.rfc-editor.org/rfc/rfc6530#section-7.1
HeaderEncodingNone headerEncoding = iota

// TODO: Add Base64 encoding
// HeaderEncodingBase64 sets the message header encoding to base64
// https://www.rfc-editor.org/rfc/rfc2045#section-6.8
// HeaderEncodingBase64

// HeaderEncodingQ sets the message header encoding to Q encoding
// https://www.rfc-editor.org/rfc/rfc2047#section-4.2
HeaderEncodingQ
)

type encoding int

const (
Expand Down Expand Up @@ -205,9 +223,10 @@ func (dsn DSN) String() string {
// NewMSG creates a new email. It uses UTF-8 by default. All charsets: http://webcheatsheet.com/HTML/character_sets_list.php
func NewMSG() *Email {
email := &Email{
headers: make(textproto.MIMEHeader),
Charset: "UTF-8",
Encoding: EncodingQuotedPrintable,
headers: make(textproto.MIMEHeader),
Charset: "UTF-8",
Encoding: EncodingQuotedPrintable,
HeaderEncoding: HeaderEncodingQ,
}

email.AddHeader("MIME-Version", "1.0")
Expand Down
36 changes: 9 additions & 27 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,21 @@ import (
type encoder struct {
w *bufio.Writer
charset string
encoding headerEncoding
usedChars int
}

// newEncoder returns a new mime header encoder that writes to w. The c
// parameter specifies the name of the character set of the text that will be
// encoded. The u parameter indicates how many characters have been used
// already.
func newEncoder(w io.Writer, c string, u int) *encoder {
return &encoder{bufio.NewWriter(w), strings.ToUpper(c), u}
func newEncoder(w io.Writer, c string, encoding headerEncoding, u int) *encoder {
return &encoder{bufio.NewWriter(w), strings.ToUpper(c), encoding, u}
}

// encode encodes p using the "Q" encoding and writes it to the underlying
// io.Writer. It limits line length to 75 characters.
// encode encodes p using the encoding scheme specified in e
// If all chars are printable ascii chars, no encoding is performed.
// Limits line length to 75 characters and folds lines as necessary.
func (e *encoder) encode(p []byte) (n int, err error) {
var output bytes.Buffer
allPrintable := true
Expand All @@ -48,7 +50,7 @@ func (e *encoder) encode(p []byte) (n int, err error) {
}

// all characters are printable. just do line folding
if allPrintable {
if allPrintable || e.encoding == HeaderEncodingNone {
text := string(p)
words := strings.Split(text, " ")

Expand All @@ -57,18 +59,14 @@ func (e *encoder) encode(p []byte) (n int, err error) {

// split the line where necessary
for _, word := range words {
/*fmt.Println("Current Line:",lineBuffer)
fmt.Println("Here: Max:", maxLineLength ,"Buffer Length:", len(lineBuffer), "Used Chars:", e.usedChars, "Length Encoded Char:",len(word))
fmt.Println("----------")*/

newWord := ""
if !firstWord {
newWord += " "
}
newWord += word

// check line length
if (e.usedChars+len(lineBuffer)+len(newWord) /*+len(" ")+len(word)*/) > maxLineLength && (lineBuffer != "" || e.usedChars != 0) {
if (e.usedChars+len(lineBuffer)+len(newWord)) > maxLineLength && (lineBuffer != "" || e.usedChars != 0) {
output.WriteString(lineBuffer + "\r\n")

// first word on newline needs a space in front
Expand All @@ -78,29 +76,17 @@ func (e *encoder) encode(p []byte) (n int, err error) {
lineBuffer = " "
}

//firstLine = false
//firstWord = true
// reset since not on the first line anymore
e.usedChars = 0
}

/*if !firstWord {
lineBuffer += " "
}*/

lineBuffer += newWord /*word*/

firstWord = false

// reset since not on the first line anymore
/*if !firstLine {
e.usedChars = 0
}*/
}

output.WriteString(lineBuffer)

} else {
// else block can only be HeaderEncodingQ as of now
firstLine := true

// A single encoded word can not be longer than 75 characters
Expand All @@ -117,10 +103,6 @@ func (e *encoder) encode(p []byte) (n int, err error) {
// encode the character
encodedChar, runeLength := encode(p, i)

/*fmt.Println("Current Line:",lineBuffer)
fmt.Println("Here: Max:", maxLineLength ,"Buffer Length:", len(lineBuffer), "Used Chars:", e.usedChars, "Length Encoded Char:",len(encodedChar))
fmt.Println("----------")*/

// Check line length
if len(lineBuffer)+e.usedChars+len(encodedChar) > (maxLineLength - len(wordEnd)) {
output.WriteString(lineBuffer + wordEnd + "\r\n")
Expand Down
Loading

0 comments on commit 3161d31

Please sign in to comment.