mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			162 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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 (
 | 
						|
	"bytes"
 | 
						|
	"errors"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/textproto"
 | 
						|
	"os"
 | 
						|
)
 | 
						|
 | 
						|
// 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 of the file parts in memory
 | 
						|
// and the remainder on disk in temporary files.
 | 
						|
func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
 | 
						|
	return r.readForm(maxMemory)
 | 
						|
}
 | 
						|
 | 
						|
func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
 | 
						|
	form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
 | 
						|
	defer func() {
 | 
						|
		if err != nil {
 | 
						|
			form.RemoveAll()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	maxValueBytes := int64(10 << 20) // 10 MB is a lot of text.
 | 
						|
	for {
 | 
						|
		p, err := r.NextPart()
 | 
						|
		if err == io.EOF {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		name := p.FormName()
 | 
						|
		if name == "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		filename := p.FileName()
 | 
						|
 | 
						|
		var b bytes.Buffer
 | 
						|
 | 
						|
		if filename == "" {
 | 
						|
			// value, store as string in memory
 | 
						|
			n, err := io.CopyN(&b, p, maxValueBytes)
 | 
						|
			if err != nil && err != io.EOF {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			maxValueBytes -= n
 | 
						|
			if maxValueBytes == 0 {
 | 
						|
				return nil, errors.New("multipart: message too large")
 | 
						|
			}
 | 
						|
			form.Value[name] = append(form.Value[name], b.String())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// file, store in memory or on disk
 | 
						|
		fh := &FileHeader{
 | 
						|
			Filename: filename,
 | 
						|
			Header:   p.Header,
 | 
						|
		}
 | 
						|
		n, err := io.CopyN(&b, p, maxMemory+1)
 | 
						|
		if err != nil && err != io.EOF {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if n > maxMemory {
 | 
						|
			// too big, write to disk and flush buffer
 | 
						|
			file, err := ioutil.TempFile("", "multipart-")
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			defer file.Close()
 | 
						|
			_, err = io.Copy(file, io.MultiReader(&b, p))
 | 
						|
			if err != nil {
 | 
						|
				os.Remove(file.Name())
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			fh.tmpfile = file.Name()
 | 
						|
		} else {
 | 
						|
			fh.content = b.Bytes()
 | 
						|
			maxMemory -= n
 | 
						|
		}
 | 
						|
		form.File[name] = append(form.File[name], fh)
 | 
						|
	}
 | 
						|
 | 
						|
	return form, nil
 | 
						|
}
 | 
						|
 | 
						|
// 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 (f *Form) RemoveAll() error {
 | 
						|
	var err error
 | 
						|
	for _, fhs := range f.File {
 | 
						|
		for _, fh := range fhs {
 | 
						|
			if fh.tmpfile != "" {
 | 
						|
				e := os.Remove(fh.tmpfile)
 | 
						|
				if e != nil && err == nil {
 | 
						|
					err = e
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// A FileHeader describes a file part of a multipart request.
 | 
						|
type FileHeader struct {
 | 
						|
	Filename string
 | 
						|
	Header   textproto.MIMEHeader
 | 
						|
 | 
						|
	content []byte
 | 
						|
	tmpfile string
 | 
						|
}
 | 
						|
 | 
						|
// Open opens and returns the FileHeader's associated File.
 | 
						|
func (fh *FileHeader) Open() (File, error) {
 | 
						|
	if b := fh.content; b != nil {
 | 
						|
		r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
 | 
						|
		return sectionReadCloser{r}, nil
 | 
						|
	}
 | 
						|
	return os.Open(fh.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
 | 
						|
}
 | 
						|
 | 
						|
func (rc sectionReadCloser) Close() error {
 | 
						|
	return nil
 | 
						|
}
 |