Repository: jhillyerd/enmime
Pull request #148 (2020-02-21 17:40:32)

Adds Part.ContentTypeParams to allow custom params
 Custom ContentTypeParams are required in certain situations.
 E.g. RFC3156 (MIME Security for OpenPGP) requires
 protocol="application/pgp-encrypted" as Content-Type param.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/28884632/badge)](https://coveralls.io/builds/28884632)

Coverage decreased (-0.09%) to 95.472% when pulling **fcfaa5c061514f53fa18a8ead7efdb3414164e7a on pzeinlinger:feature/ContentTypeParams** into **6c3c516140b38fa7d73e6f9cfe6bd6225934472a on jhillyerd:develop**.

jhillyerd: Thanks, this is a good idea!

Can you add a unit test?  Perhaps extend https://github.com/jhillyerd/enmime/blob/master/encode_test.go#L34 and its golden file.  That way we can ensure any future refactoring doesn't break this functionality.
pzeinlinger: Sure thing!

Patch:
diff --git a/encode.go b/encode.go
index 2b408c8..e54ac13 100644
--- a/encode.go
+++ b/encode.go
@@ -100,6 +100,9 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
 	if p.ContentType != "" {
 		// Build content type header.
 		param := make(map[string]string)
+		for k, v := range p.ContentTypeParams {
+			param[k] = v
+		}
 		setParamValue(param, hpCharset, p.Charset)
 		setParamValue(param, hpName, stringutil.ToASCII(p.FileName))
 		setParamValue(param, hpBoundary, p.Boundary)
diff --git a/part.go b/part.go
index 05a8ef3..afc6134 100644
--- a/part.go
+++ b/part.go
@@ -31,14 +31,15 @@ type Part struct {
 	NextSibling *Part                // NextSibling of this part.
 	Header      textproto.MIMEHeader // Header for this Part.
 
-	Boundary    string    // Boundary marker used within this part.
-	ContentID   string    // ContentID header for cid URL scheme.
-	ContentType string    // ContentType header without parameters.
-	Disposition string    // Content-Disposition header without parameters.
-	FileName    string    // The file-name from disposition or type header.
-	FileModDate time.Time // The modification date of the file.
-	Charset     string    // The content charset encoding, may differ from charset in header.
-	OrigCharset string    // The original content charset when a different charset was detected.
+	Boundary          string            // Boundary marker used within this part.
+	ContentID         string            // ContentID header for cid URL scheme.
+	ContentType       string            // ContentType header without parameters.
+	ContentTypeParams map[string]string // Params, added to ContentType header.
+	Disposition       string            // Content-Disposition header without parameters.
+	FileName          string            // The file-name from disposition or type header.
+	FileModDate       time.Time         // The modification date of the file.
+	Charset           string            // The content charset encoding, may differ from charset in header.
+	OrigCharset       string            // The original content charset when a different charset was detected.
 
 	Errors   []*Error // Errors encountered while parsing this part.
 	Content  []byte   // Content after decoding, UTF-8 conversion if applicable.
@@ -48,8 +49,9 @@ type Part struct {
 // NewPart creates a new Part object.
 func NewPart(contentType string) *Part {
 	return &Part{
-		Header:      make(textproto.MIMEHeader),
-		ContentType: contentType,
+		Header:            make(textproto.MIMEHeader),
+		ContentType:       contentType,
+		ContentTypeParams: make(map[string]string),
 	}
 }
 


Test patch:
diff --git a/encode_test.go b/encode_test.go
index 806c167..c4e39db 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -48,6 +48,8 @@ func TestEncodePartDefaultHeaders(t *testing.T) {
 	p.Boundary = "enmime-abcdefg0123456789"
 	p.Charset = "binary"
 	p.ContentID = "mycontentid"
+	p.ContentTypeParams["param1"] = "myparameter1"
+	p.ContentTypeParams["param2"] = "myparameter2"
 	p.Disposition = "attachment"
 	p.FileName = "stuff.zip"
 	p.FileModDate, _ = time.Parse(time.RFC822, "01 Feb 03 04:05 GMT")
@@ -66,6 +68,8 @@ func TestEncodePartQuotedHeaders(t *testing.T) {
 	p.Boundary = "enmime-abcdefg0123456789"
 	p.Charset = "binary"
 	p.ContentID = "mycontentid"
+	p.ContentTypeParams["param1"] = "myparameter1"
+	p.ContentTypeParams["param2"] = "myparameter2"
 	p.Disposition = "attachment"
 	p.FileName = `árvíztűrő "x" tükörfúrógép.zip`
 	p.FileModDate, _ = time.Parse(time.RFC822, "01 Feb 03 04:05 GMT")
@@ -84,6 +88,8 @@ func TestEncodePartQuotedPrintableHeaders(t *testing.T) {
 	p.Boundary = "enmime-abcdefg0123456789"
 	p.Charset = "binary"
 	p.ContentID = "mycontentid"
+	p.ContentTypeParams["param1"] = "myparameter1"
+	p.ContentTypeParams["param2"] = "myparameter2"
 	p.Disposition = "attachment"
 	p.FileName = `árvíztűrő "x" tükörfúrógép.zip`
 	p.FileModDate, _ = time.Parse(time.RFC822, "01 Feb 03 04:05 GMT")
diff --git a/testdata/encode/part-default-headers.golden b/testdata/encode/part-default-headers.golden
index 382dcfd..59686cb 100644
--- a/testdata/encode/part-default-headers.golden
+++ b/testdata/encode/part-default-headers.golden
@@ -3,6 +3,6 @@ Content-Disposition: attachment; filename=stuff.zip; modification-date="01
 Content-Id: <mycontentid>
 Content-Transfer-Encoding: base64
 Content-Type: application/zip; boundary=enmime-abcdefg0123456789;
- charset=binary; name=stuff.zip
+ charset=binary; name=stuff.zip; param1=myparameter1; param2=myparameter2
 
 WklQWklQWklQ
diff --git a/testdata/encode/part-quoted-headers.golden b/testdata/encode/part-quoted-headers.golden
index d7c6293..ea68ccf 100644
--- a/testdata/encode/part-quoted-headers.golden
+++ b/testdata/encode/part-quoted-headers.golden
@@ -3,6 +3,7 @@ Content-Disposition: attachment; filename="arvizturo \"x\"
 Content-Id: <mycontentid>
 Content-Transfer-Encoding: base64
 Content-Type: application/zip; boundary=enmime-abcdefg0123456789;
- charset=binary; name="arvizturo \"x\" tukorfurogep.zip"
+ charset=binary; name="arvizturo \"x\" tukorfurogep.zip";
+ param1=myparameter1; param2=myparameter2
 
 WklQWklQWklQ
diff --git a/testdata/encode/part-quoted-printable-headers.golden b/testdata/encode/part-quoted-printable-headers.golden
index f468c7d..4ea7a33 100644
--- a/testdata/encode/part-quoted-printable-headers.golden
+++ b/testdata/encode/part-quoted-printable-headers.golden
@@ -3,7 +3,8 @@ Content-Disposition: attachment; filename="arvizturo \"x\"
 Content-Id: <mycontentid>
 Content-Transfer-Encoding: base64
 Content-Type: application/zip; boundary=enmime-abcdefg0123456789;
- charset=binary; name="arvizturo \"x\" tukorfurogep.zip"
+ charset=binary; name="arvizturo \"x\" tukorfurogep.zip";
+ param1=myparameter1; param2=myparameter2
 X-Qp-Header: =?utf-8?q?Just_enough_to_need_qp_=E2=98=86?=
 
 WklQWklQWklQ