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 RFC2046.
The implementation is sufficient for HTTP (RFC 2388) and the multipartbodies generated by popular browsers.
package multipart

import (
	
	
	
	
	
	
	
	
)

var emptyParams = make(map[string]string)
This constant needs to be at least 76 for this package to work correctly. This is because \r\n--separator_of_len_70- would fill the buffer and it wouldn't be safe to consume a single byte from it.
const peekBufferSize = 4096
A Part represents a single part in a multipart body.
The headers of the body, if any, with the keys canonicalized in the same fashion that the Go http.Request headers are. For example, "foo-bar" changes case to "Foo-Bar"
r is either a reader directly reading from mr, or it's a wrapper around such a reader, decoding the Content-Transfer-Encoding
	r io.Reader

	n       int   // known data bytes waiting in mr.bufReader
	total   int64 // total data bytes read already
	err     error // error to return when n == 0
	readErr error // read error observed from mr.bufReader
}
FormName returns the name parameter if p has a Content-Disposition of type "form-data". Otherwise it returns the empty string.
See https://tools.ietf.org/html/rfc2183 section 2 for EBNF of Content-Disposition value format.
	if .dispositionParams == nil {
		.parseContentDisposition()
	}
	if .disposition != "form-data" {
		return ""
	}
	return .dispositionParams["name"]
}
FileName returns the filename parameter of the Part's Content-Disposition header.
func ( *Part) () string {
	if .dispositionParams == nil {
		.parseContentDisposition()
	}
	return .dispositionParams["filename"]
}

func ( *Part) () {
	 := .Header.Get("Content-Disposition")
	var  error
	.disposition, .dispositionParams,  = mime.ParseMediaType()
	if  != nil {
		.dispositionParams = emptyParams
	}
}
NewReader creates a new multipart Reader reading from r using the given MIME boundary. The boundary is usually obtained from the "boundary" parameter of the message's "Content-Type" header. Use mime.ParseMediaType to parse such headers.
func ( io.Reader,  string) *Reader {
	 := []byte("\r\n--" +  + "--")
	return &Reader{
		bufReader:        bufio.NewReaderSize(&stickyErrorReader{r: }, peekBufferSize),
		nl:               [:2],
		nlDashBoundary:   [:len()-2],
		dashBoundaryDash: [2:],
		dashBoundary:     [2 : len()-2],
	}
}
stickyErrorReader is an io.Reader which never calls Read on its underlying Reader once an error has been seen. (the io.Reader interface's contract promises nothing about the return values of Read calls after an error, yet this package does do multiple Reads after error)
type stickyErrorReader struct {
	r   io.Reader
	err error
}

func ( *stickyErrorReader) ( []byte) ( int,  error) {
	if .err != nil {
		return 0, .err
	}
	, .err = .r.Read()
	return , .err
}

func ( *Reader,  bool) (*Part, error) {
	 := &Part{
		Header: make(map[string][]string),
		mr:     ,
	}
	if  := .populateHeaders();  != nil {
		return nil, 
	}
	.r = partReader{}
rawPart is used to switch between Part.NextPart and Part.NextRawPart.
	if ! {
		const  = "Content-Transfer-Encoding"
		if strings.EqualFold(.Header.Get(), "quoted-printable") {
			.Header.Del()
			.r = quotedprintable.NewReader(.r)
		}
	}
	return , nil
}

func ( *Part) () error {
	 := textproto.NewReader(.mr.bufReader)
	,  := .ReadMIMEHeader()
	if  == nil {
		.Header = 
	}
	return 
}
Read reads the body of a part, after its headers and before the next part (if any) begins.
func ( *Part) ( []byte) ( int,  error) {
	return .r.Read()
}
partReader implements io.Reader by reading raw bytes directly from the wrapped *Part, without doing any Transfer-Encoding decoding.
type partReader struct {
	p *Part
}

func ( partReader) ( []byte) (int, error) {
	 := .p
	 := .mr.bufReader
Read into buffer until we identify some data to return, or we find a reason to stop (boundary or read error).
	for .n == 0 && .err == nil {
		,  := .Peek(.Buffered())
		.n, .err = scanUntilBoundary(, .mr.dashBoundary, .mr.nlDashBoundary, .total, .readErr)
Force buffered I/O to read more into buffer.
			_, .readErr = .Peek(len() + 1)
			if .readErr == io.EOF {
				.readErr = io.ErrUnexpectedEOF
			}
		}
	}
Read out from "data to return" part of buffer.
	if .n == 0 {
		return 0, .err
	}
	 := len()
	if  > .n {
		 = .n
	}
	, _ = .Read([:])
	.total += int64()
	.n -= 
	if .n == 0 {
		return , .err
	}
	return , nil
}
scanUntilBoundary scans buf to identify how much of it can be safely returned as part of the Part body. dashBoundary is "--boundary". nlDashBoundary is "\r\n--boundary" or "\n--boundary", depending on what mode we are in. The comments below (and the name) assume "\n--boundary", but either is accepted. total is the number of bytes read out so far. If total == 0, then a leading "--boundary" is recognized. readErr is the read error, if any, that followed reading the bytes in buf. scanUntilBoundary returns the number of data bytes from buf that can be returned as part of the Part body and also the error to return (if any) once those data bytes are done.
func (, ,  []byte,  int64,  error) (int, error) {
At beginning of body, allow dashBoundary.
		if bytes.HasPrefix(, ) {
			switch matchAfterPrefix(, , ) {
			case -1:
				return len(), nil
			case 0:
				return 0, nil
			case +1:
				return 0, io.EOF
			}
		}
		if bytes.HasPrefix(, ) {
			return 0, 
		}
	}
Search for "\n--boundary".
	if  := bytes.Index(, );  >= 0 {
		switch matchAfterPrefix([:], , ) {
		case -1:
			return  + len(), nil
		case 0:
			return , nil
		case +1:
			return , io.EOF
		}
	}
	if bytes.HasPrefix(, ) {
		return 0, 
	}
Otherwise, anything up to the final \n is not part of the boundary and so must be part of the body. Also if the section from the final \n onward is not a prefix of the boundary, it too must be part of the body.
	 := bytes.LastIndexByte(, [0])
	if  >= 0 && bytes.HasPrefix(, [:]) {
		return , nil
	}
	return len(), 
}
matchAfterPrefix checks whether buf should be considered to match the boundary. The prefix is "--boundary" or "\r\n--boundary" or "\n--boundary", and the caller has verified already that bytes.HasPrefix(buf, prefix) is true. matchAfterPrefix returns +1 if the buffer does match the boundary, meaning the prefix is followed by a dash, space, tab, cr, nl, or end of input. It returns -1 if the buffer definitely does NOT match the boundary, meaning the prefix is followed by some other character. For example, "--foobar" does not match "--foo". It returns 0 more input needs to be read to make the decision, meaning that len(buf) == len(prefix) and readErr == nil.
func (,  []byte,  error) int {
	if len() == len() {
		if  != nil {
			return +1
		}
		return 0
	}
	 := [len()]
	if  == ' ' ||  == '\t' ||  == '\r' ||  == '\n' ||  == '-' {
		return +1
	}
	return -1
}

func ( *Part) () error {
	io.Copy(io.Discard, )
	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               []byte // "\r\n" or "\n" (set after seeing first boundary line)
	nlDashBoundary   []byte // nl + "--boundary"
	dashBoundaryDash []byte // "--boundary--"
	dashBoundary     []byte // "--boundary"
}
NextPart returns the next part in the multipart or an error. When there are no more parts, the error io.EOF is returned. As a special case, if the "Content-Transfer-Encoding" header has a value of "quoted-printable", that header is instead hidden and the body is transparently decoded during Read calls.
func ( *Reader) () (*Part, error) {
	return .nextPart(false)
}
NextRawPart returns the next part in the multipart or an error. When there are no more parts, the error io.EOF is returned. Unlike NextPart, it does not have special handling for "Content-Transfer-Encoding: quoted-printable".
func ( *Reader) () (*Part, error) {
	return .nextPart(true)
}

func ( *Reader) ( bool) (*Part, error) {
	if .currentPart != nil {
		.currentPart.Close()
	}
	if string(.dashBoundary) == "--" {
		return nil, fmt.Errorf("multipart: boundary is empty")
	}
	 := false
	for {
		,  := .bufReader.ReadSlice('\n')

If the buffer ends in "--boundary--" without the trailing "\r\n", ReadSlice will return an error (since it's missing the '\n'), but this is a valid multipart EOF so we need to return io.EOF instead of a fmt-wrapped one.
			return nil, io.EOF
		}
		if  != nil {
			return nil, fmt.Errorf("multipart: NextPart: %v", )
		}

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

Expected EOF
			return nil, io.EOF
		}

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

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(, .nl) {
			 = true
			continue
		}

		return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", )
	}
}
isFinalBoundary reports whether line is the final boundary line indicating that all parts are over. It matches `^--boundary--[ \t]*(\r\n)?$`
func ( *Reader) ( []byte) bool {
	if !bytes.HasPrefix(, .dashBoundaryDash) {
		return false
	}
	 := [len(.dashBoundaryDash):]
	 = skipLWSPChar()
	return len() == 0 || bytes.Equal(, .nl)
}

https://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(, .dashBoundary) {
		return false
	}
	 := [len(.dashBoundary):]
	 = skipLWSPChar()
On the first part, see our lines are ending in \n instead of \r\n and switch into that mode if so. This is a violation of the spec, but occurs in practice.
	if .partsRead == 0 && len() == 1 && [0] == '\n' {
		.nl = .nl[1:]
		.nlDashBoundary = .nlDashBoundary[1:]
	}
	return bytes.Equal(, .nl)
}
skipLWSPChar returns b with leading spaces and tabs removed. RFC 822 defines: LWSP-char = SPACE / HTAB
func ( []byte) []byte {
	for len() > 0 && ([0] == ' ' || [0] == '\t') {
		 = [1:]
	}
	return