Repository: jhillyerd/enmime
Pull request #140 (2019-12-21 19:08:38)

mime: expected slash after first token
Found occurrences of a media parameter list without a mime type, this will inject a safe default mime type.

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

Coverage decreased (-0.3%) to 92.126% when pulling **a137fa535056d4b3feff46f8544e65a46d307545 on requaos:requaos/expectedSlashAfterToken** into **cf488a7d428e11c06fa07f9e14e1ac27460347c0 on jhillyerd:develop**.

requaos: Started getting emails from a particular source, that formats the inline attachment content-type like this:
`charset=binary; name="image.jpg"`

Resolved by just prepending a safe default mime type to it:
`application/octet-stream; charset=binary; name="image.jpg"`

Patch:
diff --git a/header.go b/header.go
index 778dd02..a36a0af 100644
--- a/header.go
+++ b/header.go
@@ -277,6 +277,11 @@ func fixMangledMediaType(mtype, sep string) string {
 	}
 	parts := strings.Split(mtype, sep)
 	mtype = ""
+	if strings.Contains(parts[0], "=") {
+		// A parameter pair at this position indicates we are missing a content-type.
+		parts[0] = fmt.Sprintf("%s%s %s", ctAppOctetStream, sep, parts[0])
+		parts = strings.Split(strings.Join(parts, sep), sep)
+	}
 	for i, p := range parts {
 		switch i {
 		case 0:
@@ -383,6 +388,13 @@ findValueStart:
 
 			break findValueStart
 
+		case ';':
+			if value.Len() == 0 {
+				value.WriteString(`"";`)
+			}
+
+			break findValueStart
+
 		default:
 			valueQuotedOriginally = false
 			valueQuoteAdded = false
@@ -400,8 +412,6 @@ findValueStart:
 	} else {
 		// The beginning of the value is not at the end of the string
 
-		s = s[i+1:]
-
 		quoteIfUnquoted := func() {
 			if !valueQuoteNeeded {
 				if !valueQuoteAdded {
@@ -414,6 +424,14 @@ findValueStart:
 			}
 		}
 
+		for _, v := range []byte{'(', ')', '<', '>', '@', ',', ':', '/', '[', ']', '?', '='} {
+			if s[0] == v {
+				quoteIfUnquoted()
+			}
+		}
+
+		s = s[i+1:]
+
 	findValueEnd:
 		for len(s) > 0 {
 			switch s[0] {


Test patch:
diff --git a/header_test.go b/header_test.go
index 11c1e11..1c9e740 100644
--- a/header_test.go
+++ b/header_test.go
@@ -214,6 +214,11 @@ func TestFixMangledMediaType(t *testing.T) {
 			sep:   ";",
 			want:  ctPlaceholder + "; name=\"file.two\"",
 		},
+		{
+			input: "charset=binary; name=\"logoleft.jpg\"",
+			sep:   ";",
+			want:  "application/octet-stream; charset=binary; name=\"logoleft.jpg\"",
+		},
 		{
 			input: "one/two;iso-8859-1",
 			sep:   ";",
@@ -323,6 +328,10 @@ func TestFixUnquotedSpecials(t *testing.T) {
 			input: "application/octet-stream; param1=value/1",
 			want:  "application/octet-stream; param1=\"value/1\"",
 		},
+		{
+			input: "multipart/alternative; boundary=?UOAwFjScLp1is-162467503201177404728935166502-",
+			want:  "multipart/alternative; boundary=\"?UOAwFjScLp1is-162467503201177404728935166502-\"",
+		},
 		{
 			input: `text/HTML; charset="UTF-8Return-Path: bounce-810_HTML-769869545-477063-1070564-43@bounce.email.oflce57578375.com`,
 			want:  `text/HTML; charset="UTF-8Return-Path: bounce-810_HTML-769869545-477063-1070564-43@bounce.email.oflce57578375.com"`,
@@ -331,6 +340,10 @@ func TestFixUnquotedSpecials(t *testing.T) {
 			input: `text/html;charset=`,
 			want:  `text/html;charset=""`,
 		},
+		{
+			input: `text/html; charset=; format=flowed`,
+			want:  `text/html; charset=""; format=flowed`,
+		},
 		{
 			input: `text/html;charset="`,
 			want:  `text/html;charset=""`,