Source file src/pkg/mime/multipart/formdata.go
1 // Copyright 2011 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 package multipart
6
7 import (
8 "bytes"
9 "errors"
10 "io"
11 "io/ioutil"
12 "net/textproto"
13 "os"
14 )
15
16 // TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here
17 // with that of the http package's ParseForm.
18
19 // ReadForm parses an entire multipart message whose parts have
20 // a Content-Disposition of "form-data".
21 // It stores up to maxMemory bytes of the file parts in memory
22 // and the remainder on disk in temporary files.
23 func (r *Reader) ReadForm(maxMemory int64) (f *Form, err error) {
24 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
25 defer func() {
26 if err != nil {
27 form.RemoveAll()
28 }
29 }()
30
31 maxValueBytes := int64(10 << 20) // 10 MB is a lot of text.
32 for {
33 p, err := r.NextPart()
34 if err == io.EOF {
35 break
36 }
37 if err != nil {
38 return nil, err
39 }
40
41 name := p.FormName()
42 if name == "" {
43 continue
44 }
45 filename := p.FileName()
46
47 var b bytes.Buffer
48
49 if filename == "" {
50 // value, store as string in memory
51 n, err := io.CopyN(&b, p, maxValueBytes)
52 if err != nil && err != io.EOF {
53 return nil, err
54 }
55 maxValueBytes -= n
56 if maxValueBytes == 0 {
57 return nil, errors.New("multipart: message too large")
58 }
59 form.Value[name] = append(form.Value[name], b.String())
60 continue
61 }
62
63 // file, store in memory or on disk
64 fh := &FileHeader{
65 Filename: filename,
66 Header: p.Header,
67 }
68 n, err := io.CopyN(&b, p, maxMemory+1)
69 if err != nil && err != io.EOF {
70 return nil, err
71 }
72 if n > maxMemory {
73 // too big, write to disk and flush buffer
74 file, err := ioutil.TempFile("", "multipart-")
75 if err != nil {
76 return nil, err
77 }
78 defer file.Close()
79 _, err = io.Copy(file, io.MultiReader(&b, p))
80 if err != nil {
81 os.Remove(file.Name())
82 return nil, err
83 }
84 fh.tmpfile = file.Name()
85 } else {
86 fh.content = b.Bytes()
87 maxMemory -= n
88 }
89 form.File[name] = append(form.File[name], fh)
90 }
91
92 return form, nil
93 }
94
95 // Form is a parsed multipart form.
96 // Its File parts are stored either in memory or on disk,
97 // and are accessible via the *FileHeader's Open method.
98 // Its Value parts are stored as strings.
99 // Both are keyed by field name.
100 type Form struct {
101 Value map[string][]string
102 File map[string][]*FileHeader
103 }
104
105 // RemoveAll removes any temporary files associated with a Form.
106 func (f *Form) RemoveAll() error {
107 var err error
108 for _, fhs := range f.File {
109 for _, fh := range fhs {
110 if fh.tmpfile != "" {
111 e := os.Remove(fh.tmpfile)
112 if e != nil && err == nil {
113 err = e
114 }
115 }
116 }
117 }
118 return err
119 }
120
121 // A FileHeader describes a file part of a multipart request.
122 type FileHeader struct {
123 Filename string
124 Header textproto.MIMEHeader
125
126 content []byte
127 tmpfile string
128 }
129
130 // Open opens and returns the FileHeader's associated File.
131 func (fh *FileHeader) Open() (File, error) {
132 if b := fh.content; b != nil {
133 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
134 return sectionReadCloser{r}, nil
135 }
136 return os.Open(fh.tmpfile)
137 }
138
139 // File is an interface to access the file part of a multipart message.
140 // Its contents may be either stored in memory or on disk.
141 // If stored on disk, the File's underlying concrete type will be an *os.File.
142 type File interface {
143 io.Reader
144 io.ReaderAt
145 io.Seeker
146 io.Closer
147 }
148
149 // helper types to turn a []byte into a File
150
151 type sectionReadCloser struct {
152 *io.SectionReader
153 }
154
155 func (rc sectionReadCloser) Close() error {
156 return nil
157 }