Skip to content

Commit

Permalink
mime: encode CTL and non-US-ASCII characters in FormatMediaType
Browse files Browse the repository at this point in the history
Encodes non-WSP CTL and non-US-ASCII UTF-8 characters using syntax specified in RFC 2231.
Fixes #7668 and #9624.
  • Loading branch information
andrius4669 committed Dec 18, 2018
1 parent 4e9b3ba commit 9c77146
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 7 deletions.
36 changes: 32 additions & 4 deletions src/mime/mediatype.go
Expand Up @@ -19,7 +19,7 @@ import (
// FormatMediaType returns the empty string.
func FormatMediaType(t string, param map[string]string) string {
var b strings.Builder
if slash := strings.Index(t, "/"); slash == -1 {
if slash := strings.IndexByte(t, '/'); slash == -1 {
if !isToken(t) {
return ""
}
Expand Down Expand Up @@ -48,7 +48,38 @@ func FormatMediaType(t string, param map[string]string) string {
return ""
}
b.WriteString(strings.ToLower(attribute))

needEnc := needsEncoding(value)
if needEnc {
// RFC 2231 section 4
b.WriteByte('*')
}
b.WriteByte('=')

if needEnc {
b.WriteString("utf-8''")

offset := 0
for index := 0; index < len(value); index++ {
ch := value[index]
// {RFC 2231 section 7}
// attribute-char := <any (US-ASCII) CHAR except SPACE, CTLs, "*", "'", "%", or tspecials>
if ch <= ' ' || ch >= 0x7F ||
ch == '*' || ch == '\'' || ch == '%' ||
isTSpecial(rune(ch)) {

b.WriteString(value[offset:index])
offset = index + 1

b.WriteByte('%')
b.WriteByte(upperhex[ch>>4])
b.WriteByte(upperhex[ch&0x0F])
}
}
b.WriteString(value[offset:])
continue
}

if isToken(value) {
b.WriteString(value)
continue
Expand All @@ -63,9 +94,6 @@ func FormatMediaType(t string, param map[string]string) string {
offset = index
b.WriteByte('\\')
}
if character&0x80 != 0 {
return ""
}
}
b.WriteString(value[offset:])
b.WriteByte('"')
Expand Down
25 changes: 22 additions & 3 deletions src/mime/mediatype_test.go
Expand Up @@ -6,6 +6,7 @@ package mime

import (
"reflect"
"strings"
"testing"
)

Expand Down Expand Up @@ -481,8 +482,9 @@ var formatTests = []formatTest{
{"noslash", map[string]string{"X": "Y"}, "noslash; x=Y"}, // e.g. Content-Disposition values (RFC 2183); issue 11289
{"foo bar/baz", nil, ""},
{"foo/bar baz", nil, ""},
{"attachment", map[string]string{"filename": "ĄĄŽŽČČŠŠ"}, ""},
{"attachment", map[string]string{"filename": "ÁÁÊÊÇÇÎÎ"}, ""},
{"attachment", map[string]string{"filename": "ĄĄŽŽČČŠŠ"}, "attachment; filename*=utf-8''%C4%84%C4%84%C5%BD%C5%BD%C4%8C%C4%8C%C5%A0%C5%A0"},
{"attachment", map[string]string{"filename": "ÁÁÊÊÇÇÎÎ"}, "attachment; filename*=utf-8''%C3%81%C3%81%C3%8A%C3%8A%C3%87%C3%87%C3%8E%C3%8E"},
{"attachment", map[string]string{"filename": "数据统计.png"}, "attachment; filename*=utf-8''%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1.png"},
{"foo/BAR", nil, "foo/bar"},
{"foo/BAR", map[string]string{"X": "Y"}, "foo/bar; x=Y"},
{"foo/BAR", map[string]string{"space": "With space"}, `foo/bar; space="With space"`},
Expand All @@ -491,7 +493,8 @@ var formatTests = []formatTest{
{"foo/BAR", map[string]string{"both": `With \backslash and "quote`}, `foo/bar; both="With \\backslash and \"quote"`},
{"foo/BAR", map[string]string{"": "empty attribute"}, ""},
{"foo/BAR", map[string]string{"bad attribute": "baz"}, ""},
{"foo/BAR", map[string]string{"nonascii": "not an ascii character: ä"}, ""},
{"foo/BAR", map[string]string{"nonascii": "not an ascii character: ä"}, "foo/bar; nonascii*=utf-8''not%20an%20ascii%20character%3A%20%C3%A4"},
{"foo/BAR", map[string]string{"ctl": "newline: \n nil: \000"}, "foo/bar; ctl*=utf-8''newline%3A%20%0A%20nil%3A%20%00"},
{"foo/bar", map[string]string{"a": "av", "b": "bv", "c": "cv"}, "foo/bar; a=av; b=bv; c=cv"},
{"foo/bar", map[string]string{"0": "'", "9": "'"}, "foo/bar; 0='; 9='"},
{"foo", map[string]string{"bar": ""}, `foo; bar=""`},
Expand All @@ -503,5 +506,21 @@ func TestFormatMediaType(t *testing.T) {
if got != tt.want {
t.Errorf("%d. FormatMediaType(%q, %v) = %q; want %q", i, tt.typ, tt.params, got, tt.want)
}
if got == "" {
continue
}
typ, params, err := ParseMediaType(got)
if err != nil {
t.Errorf("%d. ParseMediaType(%q) err: %v", i, got, err)
}
if typ != strings.ToLower(tt.typ) {
t.Errorf("%d. ParseMediaType(%q) typ = %q; want %q", i, got, typ, tt.typ)
}
for k, v := range tt.params {
k = strings.ToLower(k)
if params[k] != v {
t.Errorf("%d. ParseMediaType(%q) params[%s] = %q; want %q", i, got, k, params[k], v)
}
}
}
}

0 comments on commit 9c77146

Please sign in to comment.