diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
commit | d8f412571f8768df2d3239e72392dfeabbad1559 (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/mime | |
parent | e0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff) | |
download | gcc-d8f412571f8768df2d3239e72392dfeabbad1559.zip gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.gz gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.bz2 |
Update Go library to last weekly.
From-SVN: r180552
Diffstat (limited to 'libgo/go/mime')
-rw-r--r-- | libgo/go/mime/grammar.go | 9 | ||||
-rw-r--r-- | libgo/go/mime/mediatype.go | 77 | ||||
-rw-r--r-- | libgo/go/mime/mediatype_test.go | 19 | ||||
-rw-r--r-- | libgo/go/mime/multipart/formdata.go | 4 | ||||
-rw-r--r-- | libgo/go/mime/multipart/multipart.go | 7 | ||||
-rw-r--r-- | libgo/go/mime/multipart/writer.go | 6 | ||||
-rw-r--r-- | libgo/go/mime/type.go | 35 | ||||
-rw-r--r-- | libgo/go/mime/type_test.go (renamed from libgo/go/mime/mime_test.go) | 14 |
8 files changed, 132 insertions, 39 deletions
diff --git a/libgo/go/mime/grammar.go b/libgo/go/mime/grammar.go index 6e319ff..70a94cd 100644 --- a/libgo/go/mime/grammar.go +++ b/libgo/go/mime/grammar.go @@ -22,6 +22,15 @@ func IsTokenChar(rune int) bool { return rune > 0x20 && rune < 0x7f && !isTSpecial(rune) } +// IsToken returns true if s is a 'token' as as defined by RFC 1521 +// and RFC 2045. +func IsToken(s string) bool { + if s == "" { + return false + } + return strings.IndexFunc(s, isNotTokenChar) < 0 +} + // IsQText returns true if rune is in 'qtext' as defined by RFC 822. func IsQText(rune int) bool { // CHAR = <any ASCII character> ; ( 0-177, 0.-127.) diff --git a/libgo/go/mime/mediatype.go b/libgo/go/mime/mediatype.go index 40c735c..b0d3933 100644 --- a/libgo/go/mime/mediatype.go +++ b/libgo/go/mime/mediatype.go @@ -12,40 +12,89 @@ import ( "unicode" ) -func validMediaTypeOrDisposition(s string) bool { +// FormatMediaType serializes type t, subtype sub and the paramaters +// param as a media type conform RFC 2045 and RFC 2616. +// The type, subtype, and parameter names are written in lower-case. +// When any of the arguments result in a standard violation then +// FormatMediaType returns the empty string. +func FormatMediaType(t, sub string, param map[string]string) string { + if !(IsToken(t) && IsToken(sub)) { + return "" + } + var b bytes.Buffer + b.WriteString(strings.ToLower(t)) + b.WriteByte('/') + b.WriteString(strings.ToLower(sub)) + + for attribute, value := range param { + b.WriteByte(';') + b.WriteByte(' ') + if !IsToken(attribute) { + return "" + } + b.WriteString(strings.ToLower(attribute)) + b.WriteByte('=') + if IsToken(value) { + b.WriteString(value) + continue + } + + b.WriteByte('"') + offset := 0 + for index, character := range value { + if character == '"' || character == '\r' { + b.WriteString(value[offset:index]) + offset = index + b.WriteByte('\\') + } + if character&0x80 != 0 { + return "" + } + } + b.WriteString(value[offset:]) + b.WriteByte('"') + } + return b.String() +} + +func checkMediaTypeDisposition(s string) os.Error { typ, rest := consumeToken(s) if typ == "" { - return false + return os.NewError("mime: no media type") } if rest == "" { - return true + return nil } if !strings.HasPrefix(rest, "/") { - return false + return os.NewError("mime: expected slash after first token") } subtype, rest := consumeToken(rest[1:]) if subtype == "" { - return false + return os.NewError("mime: expected token after slash") } - return rest == "" + if rest != "" { + return os.NewError("mime: unexpected content after media subtype") + } + return nil } // ParseMediaType parses a media type value and any optional // parameters, per RFC 1521. Media types are the values in // Content-Type and Content-Disposition headers (RFC 2183). // On success, ParseMediaType returns the media type converted -// to lowercase and trimmed of white space. The returned params -// is always a non-nil map. Params maps from the lowercase +// to lowercase and trimmed of white space and a non-nil map. +// The returned map, params, maps from the lowercase // attribute to the attribute value with its case preserved. -// On error, it returns an empty string and a nil params. -func ParseMediaType(v string) (mediatype string, params map[string]string) { +func ParseMediaType(v string) (mediatype string, params map[string]string, err os.Error) { i := strings.Index(v, ";") if i == -1 { i = len(v) } mediatype = strings.TrimSpace(strings.ToLower(v[0:i])) - if !validMediaTypeOrDisposition(mediatype) { - return "", nil + + err = checkMediaTypeDisposition(mediatype) + if err != nil { + return } params = make(map[string]string) @@ -69,7 +118,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) { return } // Parse error. - return "", nil + return "", nil, os.NewError("mime: invalid media parameter") } pmap := params @@ -86,7 +135,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) { } if _, exists := pmap[key]; exists { // Duplicate parameter name is bogus. - return "", nil + return "", nil, os.NewError("mime: duplicate parameter name") } pmap[key] = value v = rest diff --git a/libgo/go/mime/mediatype_test.go b/libgo/go/mime/mediatype_test.go index 93264bd..884573e 100644 --- a/libgo/go/mime/mediatype_test.go +++ b/libgo/go/mime/mediatype_test.go @@ -219,7 +219,14 @@ func TestParseMediaType(t *testing.T) { m("firstname", "Брэд", "lastname", "Фицпатрик")}, } for _, test := range tests { - mt, params := ParseMediaType(test.in) + mt, params, err := ParseMediaType(test.in) + if err != nil { + if test.t != "" { + t.Errorf("for input %q, unexpected error: %v", test.in, err) + continue + } + continue + } if g, e := mt, test.t; g != e { t.Errorf("for input %q, expected type %q, got %q", test.in, e, g) @@ -238,11 +245,11 @@ func TestParseMediaType(t *testing.T) { } func TestParseMediaTypeBogus(t *testing.T) { - mt, params := ParseMediaType("bogus ;=========") - if mt != "" { - t.Error("expected empty type") + mt, params, err := ParseMediaType("bogus ;=========") + if err == nil { + t.Fatalf("expected an error parsing invalid media type; got type %q, params %#v", mt, params) } - if params != nil { - t.Error("expected nil params") + if err.String() != "mime: invalid media parameter" { + t.Errorf("expected invalid media parameter; got error %q", err) } } diff --git a/libgo/go/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go index 91404d6f..d114bfa 100644 --- a/libgo/go/mime/multipart/formdata.go +++ b/libgo/go/mime/multipart/formdata.go @@ -47,7 +47,7 @@ func (r *Reader) ReadForm(maxMemory int64) (f *Form, err os.Error) { if filename == "" { // value, store as string in memory - n, err := io.Copyn(&b, p, maxValueBytes) + n, err := io.CopyN(&b, p, maxValueBytes) if err != nil && err != os.EOF { return nil, err } @@ -64,7 +64,7 @@ func (r *Reader) ReadForm(maxMemory int64) (f *Form, err os.Error) { Filename: filename, Header: p.Header, } - n, err := io.Copyn(&b, p, maxMemory+1) + n, err := io.CopyN(&b, p, maxMemory+1) if err != nil && err != os.EOF { return nil, err } diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go index 2533bd3..d36e9e9 100644 --- a/libgo/go/mime/multipart/multipart.go +++ b/libgo/go/mime/multipart/multipart.go @@ -69,8 +69,9 @@ func (p *Part) FileName() string { func (p *Part) parseContentDisposition() { v := p.Header.Get("Content-Disposition") - p.disposition, p.dispositionParams = mime.ParseMediaType(v) - if p.dispositionParams == nil { + var err os.Error + p.disposition, p.dispositionParams, err = mime.ParseMediaType(v) + if err != nil { p.dispositionParams = emptyParams } } @@ -145,7 +146,7 @@ func (bp *Part) Read(p []byte) (n int, err os.Error) { return 0, io.ErrUnexpectedEOF } if nCopy > 0 { - if _, err := io.Copyn(bp.buffer, bp.mr.bufReader, int64(nCopy)); err != nil { + if _, err := io.CopyN(bp.buffer, bp.mr.bufReader, int64(nCopy)); err != nil { return 0, err } } diff --git a/libgo/go/mime/multipart/writer.go b/libgo/go/mime/multipart/writer.go index 97a8897..1bff02f 100644 --- a/libgo/go/mime/multipart/writer.go +++ b/libgo/go/mime/multipart/writer.go @@ -85,10 +85,10 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, os.Error) { return p, nil } +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + func escapeQuotes(s string) string { - s = strings.Replace(s, "\\", "\\\\", -1) - s = strings.Replace(s, "\"", "\\\"", -1) - return s + return quoteEscaper.Replace(s) } // CreateFormFile is a convenience wrapper around CreatePart. It creates diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go index 8ecfe9a..39bf40e 100644 --- a/libgo/go/mime/type.go +++ b/libgo/go/mime/type.go @@ -7,6 +7,7 @@ package mime import ( "bufio" + "fmt" "os" "strings" "sync" @@ -49,15 +50,12 @@ func loadMimeFile(filename string) { if len(fields) <= 1 || fields[0][0] == '#' { continue } - typename := fields[0] - if strings.HasPrefix(typename, "text/") { - typename += "; charset=utf-8" - } + mimeType := fields[0] for _, ext := range fields[1:] { if ext[0] == '#' { break } - mimeTypes["."+ext] = typename + setExtensionType("."+ext, mimeType) } } } @@ -81,6 +79,8 @@ var once sync.Once // /etc/mime.types // /etc/apache2/mime.types // /etc/apache/mime.types +// +// Text types have the charset parameter set to "utf-8" by default. func TypeByExtension(ext string) string { once.Do(initMime) mimeLock.RLock() @@ -93,12 +93,31 @@ func TypeByExtension(ext string) string { // the extension ext to typ. The extension should begin with // a leading dot, as in ".html". func AddExtensionType(ext, typ string) os.Error { + if ext == "" || ext[0] != '.' { + return fmt.Errorf(`mime: extension "%s" misses dot`, ext) + } once.Do(initMime) - if len(ext) < 1 || ext[0] != '.' { - return os.EINVAL + return setExtensionType(ext, typ) +} + +func setExtensionType(extension, mimeType string) os.Error { + full, param, err := ParseMediaType(mimeType) + if err != nil { + return err + } + if split := strings.Index(full, "/"); split < 0 { + return fmt.Errorf(`mime: malformed MIME type "%s"`, mimeType) + } else { + main := full[:split] + sub := full[split+1:] + if main == "text" && param["charset"] == "" { + param["charset"] = "utf-8" + } + mimeType = FormatMediaType(main, sub, param) } + mimeLock.Lock() - mimeTypes[ext] = typ + mimeTypes[extension] = mimeType mimeLock.Unlock() return nil } diff --git a/libgo/go/mime/mime_test.go b/libgo/go/mime/type_test.go index 17e6104..976f853 100644 --- a/libgo/go/mime/mime_test.go +++ b/libgo/go/mime/type_test.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Tests for type.go - package mime import "testing" @@ -14,7 +12,7 @@ var typeTests = map[string]string{ ".png": "image/png", } -func TestType(t *testing.T) { +func TestTypeByExtension(t *testing.T) { typeFiles = []string{"test.types"} for ext, want := range typeTests { @@ -25,3 +23,13 @@ func TestType(t *testing.T) { } } + +func TestCustomExtension(t *testing.T) { + custom := "text/xml; charset=iso-8859-1" + if error := AddExtensionType(".xml", custom); error != nil { + t.Fatalf("error %s for AddExtension(%s)", error, custom) + } + if registered := TypeByExtension(".xml"); registered != custom { + t.Fatalf("registered %s instead of %s", registered, custom) + } +} |