Skip to content

Commit

Permalink
feat: encoding options added (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
pgradoboev authored Jan 12, 2025
1 parent 50133b8 commit 83dff18
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 4 deletions.
13 changes: 9 additions & 4 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
} else {
cte = teBase64
if p.TextContent() && p.ContentReader == nil {
cte = selectTransferEncoding(p.Content, false)
cte = p.selectTransferEncoding(p.Content, false)
if p.Charset == "" {
p.Charset = utf8
}
Expand All @@ -144,7 +144,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
p.Header.Set(hnContentID, coding.ToIDHeader(p.ContentID))
}
fileName := p.FileName
switch selectTransferEncoding([]byte(p.FileName), true) {
switch p.selectTransferEncoding([]byte(p.FileName), true) {
case teBase64:
fileName = mime.BEncoding.Encode(utf8, p.FileName)
case teQuoted:
Expand Down Expand Up @@ -195,7 +195,7 @@ func (p *Part) encodeHeader(b *bufio.Writer) error {
for _, v := range p.Header[k] {
encv := v
if !rawContent {
switch selectTransferEncoding([]byte(v), true) {
switch p.selectTransferEncoding([]byte(v), true) {
case teBase64:
encv = mime.BEncoding.Encode(utf8, v)
case teQuoted:
Expand Down Expand Up @@ -303,10 +303,15 @@ func (p *Part) encodeContentFromReader(b *bufio.Writer) error {
}

// selectTransferEncoding scans content for non-ASCII characters and selects 'b' or 'q' encoding.
func selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
func (p *Part) selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
if len(content) == 0 {
return te7Bit
}

if p.encoder != nil && p.encoder.forceQuotedPrintableCteOption {
return teQuoted
}

// Binary chars remaining before we choose b64 encoding.
threshold := b64Percent * len(content) / 100
bincount := 0
Expand Down
16 changes: 16 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/jhillyerd/enmime/v2"
"github.com/jhillyerd/enmime/v2/internal/test"
"github.com/stretchr/testify/assert"
)

func TestEncodePartEmpty(t *testing.T) {
Expand Down Expand Up @@ -340,6 +341,21 @@ func TestEncodePartContentBinary(t *testing.T) {
test.DiffGolden(t, b.Bytes(), "testdata", "encode", "part-bin-content.golden")
}

func TestEncodePartWithForceQuotedPrintableCte(t *testing.T) {
nonASCIIcontent := bytes.Repeat([]byte{byte(0x10)}, 10)
p := enmime.NewPart("text/plain").WithEncoder(enmime.NewEncoder(enmime.ForceQuotedPrintableCte(true)))
p.Content = nonASCIIcontent
b := &bytes.Buffer{}
err := p.Encode(b)
if err != nil {
t.Fatal(err)
}

// Verify output is QP encoded.
assert.Equal(t, "quoted-printable", p.Header.Get("Content-Transfer-Encoding"))
assert.Contains(t, b.String(), "=10=10=10")
}

func TestEncodeFileModDate(t *testing.T) {
p := enmime.NewPart("text/plain")
p.Content = []byte("¡Hola, señor! Welcome to MIME")
Expand Down
33 changes: 33 additions & 0 deletions encoder_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package enmime

type EncoderOption interface {
apply(p *Encoder)
}

// Encoder implements MIME part encoding options
type Encoder struct {
forceQuotedPrintableCteOption bool
}

// ForceQuotedPrintableCte forces "quoted-printable" transfer encoding when selecting Content Transfer Encoding, preventing the use of base64.
func ForceQuotedPrintableCte(b bool) EncoderOption {
return forceQuotedPrintableCteOption(b)
}

type forceQuotedPrintableCteOption bool

func (o forceQuotedPrintableCteOption) apply(p *Encoder) {
p.forceQuotedPrintableCteOption = bool(o)
}

func NewEncoder(ops ...EncoderOption) *Encoder {
e := Encoder{
forceQuotedPrintableCteOption: false,
}

for _, o := range ops {
o.apply(&e)
}

return &e
}
9 changes: 9 additions & 0 deletions part.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type Part struct {
parser *Parser // Provides access to parsing options.

randSource rand.Source // optional rand for uuid boundary generation

encoder *Encoder // provides encoding options
}

// NewPart creates a new Part object.
Expand Down Expand Up @@ -494,3 +496,10 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
}
return nil
}

func (p *Part) WithEncoder(e *Encoder) *Part {
if e != nil {
p.encoder = e
}
return p
}

0 comments on commit 83dff18

Please sign in to comment.