OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [mime/] [multipart/] [multipart.go] - Rev 801

Go to most recent revision | Compare with Previous | Blame | View Log

// 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"
)

// 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 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")
        var err error
        p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
        if err != 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, 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() error {
        r := textproto.NewReader(bp.mr.bufReader)
        header, err := r.ReadMIMEHeader()
        if err == nil {
                bp.Header = header
        }
        return err
}

// Read reads the body of a part, after its headers and before the
// next part (if any) begins.
func (p *Part) Read(d []byte) (n int, err error) {
        if p.buffer.Len() >= len(d) {
                // Internal buffer of unconsumed data is large enough for
                // the read request.  No need to parse more at the moment.
                return p.buffer.Read(d)
        }
        peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
        unexpectedEof := err == io.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, p.mr.nlDashBoundary); idx != -1 {
                nCopy = idx
                foundBoundary = true
        } else if safeCount := len(peek) - len(p.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(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
                        return 0, err
                }
        }
        n, err = p.buffer.Read(d)
        if err == io.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 (p *Part) Close() error {
        io.Copy(ioutil.Discard, p)
        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 io.EOF is returned.
func (r *Reader) NextPart() (*Part, error) {
        if r.currentPart != nil {
                r.currentPart.Close()
        }

        expectNewPart := false
        for {
                line, err := r.bufReader.ReadSlice('\n')
                if err != nil {
                        return nil, fmt.Errorf("multipart: NextPart: %v", err)
                }

                if r.isBoundaryDelimiterLine(line) {
                        r.partsRead++
                        bp, err := newPart(r)
                        if err != nil {
                                return nil, err
                        }
                        r.currentPart = bp
                        return bp, nil
                }

                if hasPrefixThenNewline(line, r.dashBoundaryDash) {
                        // Expected EOF
                        return nil, io.EOF
                }

                if expectNewPart {
                        return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
                }

                if r.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, r.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))
}

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.