src/pkg/mime/multipart/multipart.go - The Go Programming Language

Golang

Source file src/pkg/mime/multipart/multipart.go

     1	// Copyright 2010 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	//
     5	
     6	/*
     7	Package multipart implements MIME multipart parsing, as defined in RFC
     8	2046.
     9	
    10	The implementation is sufficient for HTTP (RFC 2388) and the multipart
    11	bodies generated by popular browsers.
    12	*/
    13	package multipart
    14	
    15	import (
    16		"bufio"
    17		"bytes"
    18		"fmt"
    19		"io"
    20		"io/ioutil"
    21		"mime"
    22		"net/textproto"
    23	)
    24	
    25	// TODO(bradfitz): inline these once the compiler can inline them in
    26	// read-only situation (such as bytes.HasSuffix)
    27	var lf = []byte("\n")
    28	var crlf = []byte("\r\n")
    29	
    30	var emptyParams = make(map[string]string)
    31	
    32	// A Part represents a single part in a multipart body.
    33	type Part struct {
    34		// The headers of the body, if any, with the keys canonicalized
    35		// in the same fashion that the Go http.Request headers are.
    36		// i.e. "foo-bar" changes case to "Foo-Bar"
    37		Header textproto.MIMEHeader
    38	
    39		buffer *bytes.Buffer
    40		mr     *Reader
    41	
    42		disposition       string
    43		dispositionParams map[string]string
    44	}
    45	
    46	// FormName returns the name parameter if p has a Content-Disposition
    47	// of type "form-data".  Otherwise it returns the empty string.
    48	func (p *Part) FormName() string {
    49		// See http://tools.ietf.org/html/rfc2183 section 2 for EBNF
    50		// of Content-Disposition value format.
    51		if p.dispositionParams == nil {
    52			p.parseContentDisposition()
    53		}
    54		if p.disposition != "form-data" {
    55			return ""
    56		}
    57		return p.dispositionParams["name"]
    58	}
    59	
    60	// FileName returns the filename parameter of the Part's
    61	// Content-Disposition header.
    62	func (p *Part) FileName() string {
    63		if p.dispositionParams == nil {
    64			p.parseContentDisposition()
    65		}
    66		return p.dispositionParams["filename"]
    67	}
    68	
    69	func (p *Part) parseContentDisposition() {
    70		v := p.Header.Get("Content-Disposition")
    71		var err error
    72		p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
    73		if err != nil {
    74			p.dispositionParams = emptyParams
    75		}
    76	}
    77	
    78	// NewReader creates a new multipart Reader reading from r using the
    79	// given MIME boundary.
    80	func NewReader(reader io.Reader, boundary string) *Reader {
    81		b := []byte("\r\n--" + boundary + "--")
    82		return &Reader{
    83			bufReader: bufio.NewReader(reader),
    84	
    85			nl:               b[:2],
    86			nlDashBoundary:   b[:len(b)-2],
    87			dashBoundaryDash: b[2:],
    88			dashBoundary:     b[2 : len(b)-2],
    89		}
    90	}
    91	
    92	func newPart(mr *Reader) (*Part, error) {
    93		bp := &Part{
    94			Header: make(map[string][]string),
    95			mr:     mr,
    96			buffer: new(bytes.Buffer),
    97		}
    98		if err := bp.populateHeaders(); err != nil {
    99			return nil, err
   100		}
   101		return bp, nil
   102	}
   103	
   104	func (bp *Part) populateHeaders() error {
   105		r := textproto.NewReader(bp.mr.bufReader)
   106		header, err := r.ReadMIMEHeader()
   107		if err == nil {
   108			bp.Header = header
   109		}
   110		return err
   111	}
   112	
   113	// Read reads the body of a part, after its headers and before the
   114	// next part (if any) begins.
   115	func (p *Part) Read(d []byte) (n int, err error) {
   116		if p.buffer.Len() >= len(d) {
   117			// Internal buffer of unconsumed data is large enough for
   118			// the read request.  No need to parse more at the moment.
   119			return p.buffer.Read(d)
   120		}
   121		peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
   122		unexpectedEof := err == io.EOF
   123		if err != nil && !unexpectedEof {
   124			return 0, fmt.Errorf("multipart: Part Read: %v", err)
   125		}
   126		if peek == nil {
   127			panic("nil peek buf")
   128		}
   129	
   130		// Search the peek buffer for "\r\n--boundary". If found,
   131		// consume everything up to the boundary. If not, consume only
   132		// as much of the peek buffer as cannot hold the boundary
   133		// string.
   134		nCopy := 0
   135		foundBoundary := false
   136		if idx := bytes.Index(peek, p.mr.nlDashBoundary); idx != -1 {
   137			nCopy = idx
   138			foundBoundary = true
   139		} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
   140			nCopy = safeCount
   141		} else if unexpectedEof {
   142			// If we've run out of peek buffer and the boundary
   143			// wasn't found (and can't possibly fit), we must have
   144			// hit the end of the file unexpectedly.
   145			return 0, io.ErrUnexpectedEOF
   146		}
   147		if nCopy > 0 {
   148			if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
   149				return 0, err
   150			}
   151		}
   152		n, err = p.buffer.Read(d)
   153		if err == io.EOF && !foundBoundary {
   154			// If the boundary hasn't been reached there's more to
   155			// read, so don't pass through an EOF from the buffer
   156			err = nil
   157		}
   158		return
   159	}
   160	
   161	func (p *Part) Close() error {
   162		io.Copy(ioutil.Discard, p)
   163		return nil
   164	}
   165	
   166	// Reader is an iterator over parts in a MIME multipart body.
   167	// Reader's underlying parser consumes its input as needed.  Seeking
   168	// isn't supported.
   169	type Reader struct {
   170		bufReader *bufio.Reader
   171	
   172		currentPart *Part
   173		partsRead   int
   174	
   175		nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
   176	}
   177	
   178	// NextPart returns the next part in the multipart or an error.
   179	// When there are no more parts, the error io.EOF is returned.
   180	func (r *Reader) NextPart() (*Part, error) {
   181		if r.currentPart != nil {
   182			r.currentPart.Close()
   183		}
   184	
   185		expectNewPart := false
   186		for {
   187			line, err := r.bufReader.ReadSlice('\n')
   188			if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
   189				// If the buffer ends in "--boundary--" without the
   190				// trailing "\r\n", ReadSlice will return an error
   191				// (since it's missing the '\n'), but this is a valid
   192				// multipart EOF so we need to return io.EOF instead of
   193				// a fmt-wrapped one.
   194				return nil, io.EOF
   195			}
   196			if err != nil {
   197				return nil, fmt.Errorf("multipart: NextPart: %v", err)
   198			}
   199	
   200			if r.isBoundaryDelimiterLine(line) {
   201				r.partsRead++
   202				bp, err := newPart(r)
   203				if err != nil {
   204					return nil, err
   205				}
   206				r.currentPart = bp
   207				return bp, nil
   208			}
   209	
   210			if hasPrefixThenNewline(line, r.dashBoundaryDash) {
   211				// Expected EOF
   212				return nil, io.EOF
   213			}
   214	
   215			if expectNewPart {
   216				return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
   217			}
   218	
   219			if r.partsRead == 0 {
   220				// skip line
   221				continue
   222			}
   223	
   224			// Consume the "\n" or "\r\n" separator between the
   225			// body of the previous part and the boundary line we
   226			// now expect will follow. (either a new part or the
   227			// end boundary)
   228			if bytes.Equal(line, r.nl) {
   229				expectNewPart = true
   230				continue
   231			}
   232	
   233			return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)
   234		}
   235		panic("unreachable")
   236	}
   237	
   238	func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
   239		// http://tools.ietf.org/html/rfc2046#section-5.1
   240		//   The boundary delimiter line is then defined as a line
   241		//   consisting entirely of two hyphen characters ("-",
   242		//   decimal value 45) followed by the boundary parameter
   243		//   value from the Content-Type header field, optional linear
   244		//   whitespace, and a terminating CRLF.
   245		if !bytes.HasPrefix(line, mr.dashBoundary) {
   246			return false
   247		}
   248		if bytes.HasSuffix(line, mr.nl) {
   249			return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
   250		}
   251		// Violate the spec and also support newlines without the
   252		// carriage return...
   253		if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
   254			if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
   255				mr.nl = mr.nl[1:]
   256				mr.nlDashBoundary = mr.nlDashBoundary[1:]
   257				return true
   258			}
   259		}
   260		return false
   261	}
   262	
   263	func onlyHorizontalWhitespace(s []byte) bool {
   264		for _, b := range s {
   265			if b != ' ' && b != '\t' {
   266				return false
   267			}
   268		}
   269		return true
   270	}
   271	
   272	func hasPrefixThenNewline(s, prefix []byte) bool {
   273		return bytes.HasPrefix(s, prefix) &&
   274			(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
   275				len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
   276	}