diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libgo/go/mime/multipart/multipart.go | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libgo/go/mime/multipart/multipart.go')
-rw-r--r-- | libgo/go/mime/multipart/multipart.go | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go new file mode 100644 index 000000000..1d855c74c --- /dev/null +++ b/libgo/go/mime/multipart/multipart.go @@ -0,0 +1,280 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +/* +Package multipart implements MIME multipart parsing, as defined in RFC +2046. + +The implementation is sufficient for HTTP (RFC 2388) and the multipart +bodies generated by popular browsers. +*/ +package multipart + +import ( + "bufio" + "bytes" + "io" + "mime" + "os" + "regexp" + "strings" +) + +var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)") + +// Reader is an iterator over parts in a MIME multipart body. +// Reader's underlying parser consumes its input as needed. Seeking +// isn't supported. +type Reader interface { + // NextPart returns the next part in the multipart, or (nil, + // nil) on EOF. An error is returned if the underlying reader + // reports errors, or on truncated or otherwise malformed + // input. + NextPart() (*Part, os.Error) +} + +// A Part represents a single part in a multipart body. +type Part struct { + // The headers of the body, if any, with the keys canonicalized + // in the same fashion that the Go http.Request headers are. + // i.e. "foo-bar" changes case to "Foo-Bar" + Header map[string]string + + buffer *bytes.Buffer + mr *multiReader +} + +// FormName returns the name parameter if p has a Content-Disposition +// of type "form-data". Otherwise it returns the empty string. +func (p *Part) FormName() string { + // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF + // of Content-Disposition value format. + v, ok := p.Header["Content-Disposition"] + if !ok { + return "" + } + d, params := mime.ParseMediaType(v) + if d != "form-data" { + return "" + } + return params["name"] +} + +// NewReader creates a new multipart Reader reading from r using the +// given MIME boundary. +func NewReader(reader io.Reader, boundary string) Reader { + return &multiReader{ + boundary: boundary, + dashBoundary: "--" + boundary, + endLine: "--" + boundary + "--", + bufReader: bufio.NewReader(reader), + } +} + +// Implementation .... + +type devNullWriter bool + +func (*devNullWriter) Write(p []byte) (n int, err os.Error) { + return len(p), nil +} + +var devNull = devNullWriter(false) + +func newPart(mr *multiReader) (bp *Part, err os.Error) { + bp = new(Part) + bp.Header = make(map[string]string) + bp.mr = mr + bp.buffer = new(bytes.Buffer) + if err = bp.populateHeaders(); err != nil { + bp = nil + } + return +} + +func (bp *Part) populateHeaders() os.Error { + for { + line, err := bp.mr.bufReader.ReadString('\n') + if err != nil { + return err + } + if line == "\n" || line == "\r\n" { + return nil + } + if matches := headerRegexp.FindStringSubmatch(line); len(matches) == 3 { + key := matches[1] + value := matches[2] + // TODO: canonicalize headers ala http.Request.Header? + bp.Header[key] = value + continue + } + return os.NewError("Unexpected header line found parsing multipart body") + } + panic("unreachable") +} + +// Read reads the body of a part, after its headers and before the +// next part (if any) begins. +func (bp *Part) Read(p []byte) (n int, err os.Error) { + for { + if bp.buffer.Len() >= len(p) { + // Internal buffer of unconsumed data is large enough for + // the read request. No need to parse more at the moment. + break + } + if !bp.mr.ensureBufferedLine() { + return 0, io.ErrUnexpectedEOF + } + if bp.mr.bufferedLineIsBoundary() { + // Don't consume this line + break + } + + // Write all of this line, except the final CRLF + s := *bp.mr.bufferedLine + if strings.HasSuffix(s, "\r\n") { + bp.mr.consumeLine() + if !bp.mr.ensureBufferedLine() { + return 0, io.ErrUnexpectedEOF + } + if bp.mr.bufferedLineIsBoundary() { + // The final \r\n isn't ours. It logically belongs + // to the boundary line which follows. + bp.buffer.WriteString(s[0 : len(s)-2]) + } else { + bp.buffer.WriteString(s) + } + break + } + if strings.HasSuffix(s, "\n") { + bp.buffer.WriteString(s) + bp.mr.consumeLine() + continue + } + return 0, os.NewError("multipart parse error during Read; unexpected line: " + s) + } + return bp.buffer.Read(p) +} + +func (bp *Part) Close() os.Error { + io.Copy(&devNull, bp) + return nil +} + +type multiReader struct { + boundary string + dashBoundary string // --boundary + endLine string // --boundary-- + + bufferedLine *string + + bufReader *bufio.Reader + currentPart *Part + partsRead int +} + +func (mr *multiReader) eof() bool { + return mr.bufferedLine == nil && + !mr.readLine() +} + +func (mr *multiReader) readLine() bool { + line, err := mr.bufReader.ReadString('\n') + if err != nil { + // TODO: care about err being EOF or not? + return false + } + mr.bufferedLine = &line + return true +} + +func (mr *multiReader) bufferedLineIsBoundary() bool { + return strings.HasPrefix(*mr.bufferedLine, mr.dashBoundary) +} + +func (mr *multiReader) ensureBufferedLine() bool { + if mr.bufferedLine == nil { + return mr.readLine() + } + return true +} + +func (mr *multiReader) consumeLine() { + mr.bufferedLine = nil +} + +func (mr *multiReader) NextPart() (*Part, os.Error) { + if mr.currentPart != nil { + mr.currentPart.Close() + } + + for { + if mr.eof() { + return nil, io.ErrUnexpectedEOF + } + + if isBoundaryDelimiterLine(*mr.bufferedLine, mr.dashBoundary) { + mr.consumeLine() + mr.partsRead++ + bp, err := newPart(mr) + if err != nil { + return nil, err + } + mr.currentPart = bp + return bp, nil + } + + if hasPrefixThenNewline(*mr.bufferedLine, mr.endLine) { + mr.consumeLine() + // Expected EOF (no error) + return nil, nil + } + + if mr.partsRead == 0 { + // skip line + mr.consumeLine() + continue + } + + return nil, os.NewError("Unexpected line in Next().") + } + panic("unreachable") +} + +func isBoundaryDelimiterLine(line, dashPrefix string) bool { + // http://tools.ietf.org/html/rfc2046#section-5.1 + // The boundary delimiter line is then defined as a line + // consisting entirely of two hyphen characters ("-", + // decimal value 45) followed by the boundary parameter + // value from the Content-Type header field, optional linear + // whitespace, and a terminating CRLF. + if !strings.HasPrefix(line, dashPrefix) { + return false + } + if strings.HasSuffix(line, "\r\n") { + return onlyHorizontalWhitespace(line[len(dashPrefix) : len(line)-2]) + } + // Violate the spec and also support newlines without the + // carriage return... + if strings.HasSuffix(line, "\n") { + return onlyHorizontalWhitespace(line[len(dashPrefix) : len(line)-1]) + } + return false +} + +func onlyHorizontalWhitespace(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] != ' ' && s[i] != '\t' { + return false + } + } + return true +} + +func hasPrefixThenNewline(s, prefix string) bool { + return strings.HasPrefix(s, prefix) && + (len(s) == len(prefix)+1 && strings.HasSuffix(s, "\n") || + len(s) == len(prefix)+2 && strings.HasSuffix(s, "\r\n")) +} |