summaryrefslogtreecommitdiff
path: root/src/pkg/mime/multipart/multipart.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/mime/multipart/multipart.go')
-rw-r--r--src/pkg/mime/multipart/multipart.go282
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))
-}