diff options
Diffstat (limited to 'src/pkg/mime/multipart/multipart.go')
-rw-r--r-- | src/pkg/mime/multipart/multipart.go | 282 |
1 files changed, 0 insertions, 282 deletions
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go deleted file mode 100644 index 4711fd78b..000000000 --- a/src/pkg/mime/multipart/multipart.go +++ /dev/null @@ -1,282 +0,0 @@ -// 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" - "fmt" - "io" - "io/ioutil" - "mime" - "net/textproto" - "os" - "regexp" -) - -// TODO(bradfitz): inline these once the compiler can inline them in -// read-only situation (such as bytes.HasSuffix) -var lf = []byte("\n") -var crlf = []byte("\r\n") - -var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)") - -var emptyParams = make(map[string]string) - -// 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 textproto.MIMEHeader - - buffer *bytes.Buffer - mr *Reader - - disposition string - dispositionParams map[string]string -} - -// 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. - if p.dispositionParams == nil { - p.parseContentDisposition() - } - if p.disposition != "form-data" { - return "" - } - return p.dispositionParams["name"] -} - - -// FileName returns the filename parameter of the Part's -// Content-Disposition header. -func (p *Part) FileName() string { - if p.dispositionParams == nil { - p.parseContentDisposition() - } - return p.dispositionParams["filename"] -} - -func (p *Part) parseContentDisposition() { - v := p.Header.Get("Content-Disposition") - p.disposition, p.dispositionParams = mime.ParseMediaType(v) - if p.dispositionParams == nil { - p.dispositionParams = emptyParams - } -} - -// NewReader creates a new multipart Reader reading from r using the -// given MIME boundary. -func NewReader(reader io.Reader, boundary string) *Reader { - b := []byte("\r\n--" + boundary + "--") - return &Reader{ - bufReader: bufio.NewReader(reader), - - nl: b[:2], - nlDashBoundary: b[:len(b)-2], - dashBoundaryDash: b[2:], - dashBoundary: b[2 : len(b)-2], - } -} - -func newPart(mr *Reader) (*Part, os.Error) { - bp := &Part{ - Header: make(map[string][]string), - mr: mr, - buffer: new(bytes.Buffer), - } - if err := bp.populateHeaders(); err != nil { - return nil, err - } - return bp, nil -} - -func (bp *Part) populateHeaders() os.Error { - for { - lineBytes, err := bp.mr.bufReader.ReadSlice('\n') - if err != nil { - return err - } - line := string(lineBytes) - if line == "\n" || line == "\r\n" { - return nil - } - if matches := headerRegexp.FindStringSubmatch(line); len(matches) == 3 { - bp.Header.Add(matches[1], matches[2]) - 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) { - 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. - return bp.buffer.Read(p) - } - peek, err := bp.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor - unexpectedEof := err == os.EOF - if err != nil && !unexpectedEof { - return 0, fmt.Errorf("multipart: Part Read: %v", err) - } - if peek == nil { - panic("nil peek buf") - } - - // Search the peek buffer for "\r\n--boundary". If found, - // consume everything up to the boundary. If not, consume only - // as much of the peek buffer as cannot hold the boundary - // string. - nCopy := 0 - foundBoundary := false - if idx := bytes.Index(peek, bp.mr.nlDashBoundary); idx != -1 { - nCopy = idx - foundBoundary = true - } else if safeCount := len(peek) - len(bp.mr.nlDashBoundary); safeCount > 0 { - nCopy = safeCount - } else if unexpectedEof { - // If we've run out of peek buffer and the boundary - // wasn't found (and can't possibly fit), we must have - // hit the end of the file unexpectedly. - return 0, io.ErrUnexpectedEOF - } - if nCopy > 0 { - if _, err := io.Copyn(bp.buffer, bp.mr.bufReader, int64(nCopy)); err != nil { - return 0, err - } - } - n, err = bp.buffer.Read(p) - if err == os.EOF && !foundBoundary { - // If the boundary hasn't been reached there's more to - // read, so don't pass through an EOF from the buffer - err = nil - } - return -} - -func (bp *Part) Close() os.Error { - io.Copy(ioutil.Discard, bp) - return nil -} - -// 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 struct { - bufReader *bufio.Reader - - currentPart *Part - partsRead int - - nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte -} - -// NextPart returns the next part in the multipart or an error. -// When there are no more parts, the error os.EOF is returned. -func (mr *Reader) NextPart() (*Part, os.Error) { - if mr.currentPart != nil { - mr.currentPart.Close() - } - - expectNewPart := false - for { - line, err := mr.bufReader.ReadSlice('\n') - if err != nil { - return nil, fmt.Errorf("multipart: NextPart: %v", err) - } - - if mr.isBoundaryDelimiterLine(line) { - mr.partsRead++ - bp, err := newPart(mr) - if err != nil { - return nil, err - } - mr.currentPart = bp - return bp, nil - } - - if hasPrefixThenNewline(line, mr.dashBoundaryDash) { - // Expected EOF - return nil, os.EOF - } - - if expectNewPart { - return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line)) - } - - if mr.partsRead == 0 { - // skip line - continue - } - - // Consume the "\n" or "\r\n" separator between the - // body of the previous part and the boundary line we - // now expect will follow. (either a new part or the - // end boundary) - if bytes.Equal(line, mr.nl) { - expectNewPart = true - continue - } - - return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line) - } - panic("unreachable") -} - -func (mr *Reader) isBoundaryDelimiterLine(line []byte) 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 !bytes.HasPrefix(line, mr.dashBoundary) { - return false - } - if bytes.HasSuffix(line, mr.nl) { - return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)]) - } - // Violate the spec and also support newlines without the - // carriage return... - if mr.partsRead == 0 && bytes.HasSuffix(line, lf) { - if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) { - mr.nl = mr.nl[1:] - mr.nlDashBoundary = mr.nlDashBoundary[1:] - return true - } - } - return false -} - -func onlyHorizontalWhitespace(s []byte) bool { - for _, b := range s { - if b != ' ' && b != '\t' { - return false - } - } - return true -} - -func hasPrefixThenNewline(s, prefix []byte) bool { - return bytes.HasPrefix(s, prefix) && - (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' || - len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf)) -} |