// Copyright 2011 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

import (
	
	
	
	
	
	
	
	
)

// ErrMessageTooLarge is returned by ReadForm if the message form
// data is too large to be processed.
var ErrMessageTooLarge = errors.New("multipart: message too large")

// TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here
// with that of the http package's ParseForm.

// ReadForm parses an entire multipart message whose parts have
// a Content-Disposition of "form-data".
// It stores up to maxMemory bytes + 10MB (reserved for non-file parts)
// in memory. File parts which can't be stored in memory will be stored on
// disk in temporary files.
// It returns ErrMessageTooLarge if all non-file parts can't be stored in
// memory.
func ( *Reader) ( int64) (*Form, error) {
	return .readForm()
}

var (
	multipartFiles    = godebug.New("#multipartfiles") // TODO: document and remove #
	multipartMaxParts = godebug.New("multipartmaxparts")
)

func ( *Reader) ( int64) ( *Form,  error) {
	 := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
	var (
		    *os.File
		 int64
	)
	 := 0
	 := true
	if multipartFiles.Value() == "distinct" {
		 = false
		// multipartFiles.IncNonDefault() // TODO: uncomment after documenting
	}
	 := 1000
	if  := multipartMaxParts.Value();  != "" {
		if ,  := strconv.Atoi();  == nil &&  >= 0 {
			 = 
			multipartMaxParts.IncNonDefault()
		}
	}
	 := maxMIMEHeaders()

	defer func() {
		if  != nil {
			if  := .Close();  == nil {
				 = 
			}
		}
		if  &&  > 1 {
			for ,  := range .File {
				for ,  := range  {
					.tmpshared = true
				}
			}
		}
		if  != nil {
			.RemoveAll()
			if  != nil {
				os.Remove(.Name())
			}
		}
	}()

	// maxFileMemoryBytes is the maximum bytes of file data we will store in memory.
	// Data past this limit is written to disk.
	// This limit strictly applies to content, not metadata (filenames, MIME headers, etc.),
	// since metadata is always stored in memory, not disk.
	//
	// maxMemoryBytes is the maximum bytes we will store in memory, including file content,
	// non-file part values, metadata, and map entry overhead.
	//
	// We reserve an additional 10 MB in maxMemoryBytes for non-file data.
	//
	// The relationship between these parameters, as well as the overly-large and
	// unconfigurable 10 MB added on to maxMemory, is unfortunate but difficult to change
	// within the constraints of the API as documented.
	 := 
	if  == math.MaxInt64 {
		--
	}
	 :=  + int64(10<<20)
	if  <= 0 {
		if  < 0 {
			 = 0
		} else {
			 = math.MaxInt64
		}
	}
	var  []byte
	for {
		,  := .nextPart(false, , )
		if  == io.EOF {
			break
		}
		if  != nil {
			return nil, 
		}
		if  <= 0 {
			return nil, ErrMessageTooLarge
		}
		--

		 := .FormName()
		if  == "" {
			continue
		}
		 := .FileName()

		// Multiple values for the same key (one map entry, longer slice) are cheaper
		// than the same number of values for different keys (many map entries), but
		// using a consistent per-value cost for overhead is simpler.
		const  = 200
		 -= int64(len())
		 -= 
		if  < 0 {
			// We can't actually take this path, since nextPart would already have
			// rejected the MIME headers for being too large. Check anyway.
			return nil, ErrMessageTooLarge
		}

		var  bytes.Buffer

		if  == "" {
			// value, store as string in memory
			,  := io.CopyN(&, , +1)
			if  != nil &&  != io.EOF {
				return nil, 
			}
			 -= 
			if  < 0 {
				return nil, ErrMessageTooLarge
			}
			.Value[] = append(.Value[], .String())
			continue
		}

		// file, store in memory or on disk
		const  = 100
		 -= mimeHeaderSize(.Header)
		 -= 
		 -= 
		if  < 0 {
			return nil, ErrMessageTooLarge
		}
		for ,  := range .Header {
			 -= int64(len())
		}
		 := &FileHeader{
			Filename: ,
			Header:   .Header,
		}
		,  := io.CopyN(&, , +1)
		if  != nil &&  != io.EOF {
			return nil, 
		}
		if  >  {
			if  == nil {
				,  = os.CreateTemp(.tempDir, "multipart-")
				if  != nil {
					return nil, 
				}
			}
			++
			if ,  := .Write(.Bytes());  != nil {
				return nil, 
			}
			if  == nil {
				 = make([]byte, 32*1024) // same buffer size as io.Copy uses
			}
			// os.File.ReadFrom will allocate its own copy buffer if we let io.Copy use it.
			type  struct{ io.Writer }
			,  := io.CopyBuffer({}, , )
			if  != nil {
				return nil, 
			}
			.tmpfile = .Name()
			.Size = int64(.Len()) + 
			.tmpoff = 
			 += .Size
			if ! {
				if  := .Close();  != nil {
					return nil, 
				}
				 = nil
			}
		} else {
			.content = .Bytes()
			.Size = int64(len(.content))
			 -= 
			 -= 
		}
		.File[] = append(.File[], )
	}

	return , nil
}

func mimeHeaderSize( textproto.MIMEHeader) ( int64) {
	 = 400
	for ,  := range  {
		 += int64(len())
		 += 200 // map entry overhead
		for ,  := range  {
			 += int64(len())
		}
	}
	return 
}

// Form is a parsed multipart form.
// Its File parts are stored either in memory or on disk,
// and are accessible via the *FileHeader's Open method.
// Its Value parts are stored as strings.
// Both are keyed by field name.
type Form struct {
	Value map[string][]string
	File  map[string][]*FileHeader
}

// RemoveAll removes any temporary files associated with a Form.
func ( *Form) () error {
	var  error
	for ,  := range .File {
		for ,  := range  {
			if .tmpfile != "" {
				 := os.Remove(.tmpfile)
				if  != nil && !errors.Is(, os.ErrNotExist) &&  == nil {
					 = 
				}
			}
		}
	}
	return 
}

// A FileHeader describes a file part of a multipart request.
type FileHeader struct {
	Filename string
	Header   textproto.MIMEHeader
	Size     int64

	content   []byte
	tmpfile   string
	tmpoff    int64
	tmpshared bool
}

// Open opens and returns the FileHeader's associated File.
func ( *FileHeader) () (File, error) {
	if  := .content;  != nil {
		 := io.NewSectionReader(bytes.NewReader(), 0, int64(len()))
		return sectionReadCloser{, nil}, nil
	}
	if .tmpshared {
		,  := os.Open(.tmpfile)
		if  != nil {
			return nil, 
		}
		 := io.NewSectionReader(, .tmpoff, .Size)
		return sectionReadCloser{, }, nil
	}
	return os.Open(.tmpfile)
}

// File is an interface to access the file part of a multipart message.
// Its contents may be either stored in memory or on disk.
// If stored on disk, the File's underlying concrete type will be an *os.File.
type File interface {
	io.Reader
	io.ReaderAt
	io.Seeker
	io.Closer
}

// helper types to turn a []byte into a File

type sectionReadCloser struct {
	*io.SectionReader
	io.Closer
}

func ( sectionReadCloser) () error {
	if .Closer != nil {
		return .Closer.Close()
	}
	return nil
}