aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/mime
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
commitd8f412571f8768df2d3239e72392dfeabbad1559 (patch)
tree19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/mime
parente0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff)
downloadgcc-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.go9
-rw-r--r--libgo/go/mime/mediatype.go77
-rw-r--r--libgo/go/mime/mediatype_test.go19
-rw-r--r--libgo/go/mime/multipart/formdata.go4
-rw-r--r--libgo/go/mime/multipart/multipart.go7
-rw-r--r--libgo/go/mime/multipart/writer.go6
-rw-r--r--libgo/go/mime/type.go35
-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)
+ }
+}