Repository: emersion/go-imap
Pull request #384 (2020-08-22 19:33:53)

imap: lower some fields + content disposition keys
Regarding #383

`TestBodyStructure_Format` does not pass right now because the BodyStructure contains lowercased values of the input string. So when it's formatted, its field list does not match the input.

I could:
1. Move the case out of `bodyStructureTests` into its own test function that does the equivalent of `TestBodyStructure_Parse` on that one case.
2. Add an optional member to the `bodyStructureTests` element struct type to allow overriding the expected value instead of `formatFields(test.fields)`.

The first option seems less freaky. I thought I'd ask in case you have better ideas or preferences.


Patch:
diff --git a/message.go b/message.go
index cb035d3..721ae02 100644
--- a/message.go
+++ b/message.go
@@ -119,6 +119,11 @@ func encodeHeader(s string) string {
 	return mime.QEncoding.Encode("utf-8", s)
 }
 
+func stringLowered(i interface{}) (string, bool) {
+	s, ok := i.(string)
+	return strings.ToLower(s), ok
+}
+
 func parseHeaderParamList(fields []interface{}) (map[string]string, error) {
 	params, err := ParseParamList(fields)
 	if err != nil {
@@ -126,8 +131,14 @@ func parseHeaderParamList(fields []interface{}) (map[string]string, error) {
 	}
 
 	for k, v := range params {
+		if lower := strings.ToLower(k); lower != k {
+			delete(params, k)
+			k = lower
+		}
+
 		params[k], _ = decodeHeader(v)
 	}
+
 	return params, nil
 }
 
@@ -911,6 +922,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
 			if disp, ok := fields[end].([]interface{}); ok && len(disp) >= 2 {
 				if s, ok := disp[0].(string); ok {
 					bs.Disposition, _ = decodeHeader(s)
+					bs.Disposition = strings.ToLower(bs.Disposition)
 				}
 				if params, ok := disp[1].([]interface{}); ok {
 					bs.DispositionParams, _ = parseHeaderParamList(params)
@@ -939,8 +951,8 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
 			return errors.New("Non-multipart body part doesn't have 7 fields")
 		}
 
-		bs.MIMEType, _ = fields[0].(string)
-		bs.MIMESubType, _ = fields[1].(string)
+		bs.MIMEType, _ = stringLowered(fields[0])
+		bs.MIMESubType, _ = stringLowered(fields[1])
 
 		params, _ := fields[2].([]interface{})
 		bs.Params, _ = parseHeaderParamList(params)
@@ -949,7 +961,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
 		if desc, err := ParseString(fields[4]); err == nil {
 			bs.Description, _ = decodeHeader(desc)
 		}
-		bs.Encoding, _ = fields[5].(string)
+		bs.Encoding, _ = stringLowered(fields[5])
 		bs.Size, _ = ParseNumber(fields[6])
 
 		end := 7
@@ -993,6 +1005,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
 			if disp, ok := fields[end].([]interface{}); ok && len(disp) >= 2 {
 				if s, ok := disp[0].(string); ok {
 					bs.Disposition, _ = decodeHeader(s)
+					bs.Disposition = strings.ToLower(bs.Disposition)
 				}
 				if params, ok := disp[1].([]interface{}); ok {
 					bs.DispositionParams, _ = parseHeaderParamList(params)


Test patch:
diff --git a/message_test.go b/message_test.go
index d08fd9f..aeae249 100644
--- a/message_test.go
+++ b/message_test.go
@@ -593,6 +593,36 @@ func TestBodyStructure_Parse(t *testing.T) {
 	}
 }
 
+func TestBodyStructure_Parse_uppercase(t *testing.T) {
+	fields := []interface{}{
+		"APPLICATION", "PDF", []interface{}{"NAME", "Document.pdf"}, nil, nil,
+		"BASE64", RawString("4242"), nil,
+		[]interface{}{"ATTACHMENT", []interface{}{"FILENAME", "Document.pdf"}},
+		nil, nil,
+	}
+
+	expected := &BodyStructure{
+		MIMEType:          "application",
+		MIMESubType:       "pdf",
+		Params:            map[string]string{"name": "Document.pdf"},
+		Encoding:          "base64",
+		Size:              4242,
+		Extended:          true,
+		MD5:               "",
+		Disposition:       "attachment",
+		DispositionParams: map[string]string{"filename": "Document.pdf"},
+		Language:          nil,
+		Location:          []string{},
+	}
+
+	bs := &BodyStructure{}
+	if err := bs.Parse(fields); err != nil {
+		t.Errorf("Cannot parse: %v", err)
+	} else if !reflect.DeepEqual(bs, expected) {
+		t.Errorf("Invalid body structure: got \n%+v\n but expected \n%+v", bs, expected)
+	}
+}
+
 func TestBodyStructure_Format(t *testing.T) {
 	for i, test := range bodyStructureTests {
 		fields := test.bodyStructure.Format()