Source file src/pkg/mime/multipart/multipart.go
1
2
3
4
5
6
13 package multipart
14
15 import (
16 "bufio"
17 "bytes"
18 "fmt"
19 "io"
20 "io/ioutil"
21 "mime"
22 "mime/quotedprintable"
23 "net/textproto"
24 "strings"
25 )
26
27 var emptyParams = make(map[string]string)
28
29
30
31
32 const peekBufferSize = 4096
33
34
35 type Part struct {
36
37
38
39
40
41
42
43
44 Header textproto.MIMEHeader
45
46 mr *Reader
47
48 disposition string
49 dispositionParams map[string]string
50
51
52
53
54 r io.Reader
55
56 n int
57 total int64
58 err error
59 readErr error
60 }
61
62
63
64 func (p *Part) FormName() string {
65
66
67 if p.dispositionParams == nil {
68 p.parseContentDisposition()
69 }
70 if p.disposition != "form-data" {
71 return ""
72 }
73 return p.dispositionParams["name"]
74 }
75
76
77
78 func (p *Part) FileName() string {
79 if p.dispositionParams == nil {
80 p.parseContentDisposition()
81 }
82 return p.dispositionParams["filename"]
83 }
84
85 func (p *Part) parseContentDisposition() {
86 v := p.Header.Get("Content-Disposition")
87 var err error
88 p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
89 if err != nil {
90 p.dispositionParams = emptyParams
91 }
92 }
93
94
95
96
97
98
99
100 func NewReader(r io.Reader, boundary string) *Reader {
101 b := []byte("\r\n--" + boundary + "--")
102 return &Reader{
103 bufReader: bufio.NewReaderSize(&stickyErrorReader{r: r}, peekBufferSize),
104 nl: b[:2],
105 nlDashBoundary: b[:len(b)-2],
106 dashBoundaryDash: b[2:],
107 dashBoundary: b[2 : len(b)-2],
108 }
109 }
110
111
112
113
114
115
116 type stickyErrorReader struct {
117 r io.Reader
118 err error
119 }
120
121 func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
122 if r.err != nil {
123 return 0, r.err
124 }
125 n, r.err = r.r.Read(p)
126 return n, r.err
127 }
128
129 func newPart(mr *Reader) (*Part, error) {
130 bp := &Part{
131 Header: make(map[string][]string),
132 mr: mr,
133 }
134 if err := bp.populateHeaders(); err != nil {
135 return nil, err
136 }
137 bp.r = partReader{bp}
138 const cte = "Content-Transfer-Encoding"
139 if strings.EqualFold(bp.Header.Get(cte), "quoted-printable") {
140 bp.Header.Del(cte)
141 bp.r = quotedprintable.NewReader(bp.r)
142 }
143 return bp, nil
144 }
145
146 func (bp *Part) populateHeaders() error {
147 r := textproto.NewReader(bp.mr.bufReader)
148 header, err := r.ReadMIMEHeader()
149 if err == nil {
150 bp.Header = header
151 }
152 return err
153 }
154
155
156
157 func (p *Part) Read(d []byte) (n int, err error) {
158 return p.r.Read(d)
159 }
160
161
162
163 type partReader struct {
164 p *Part
165 }
166
167 func (pr partReader) Read(d []byte) (int, error) {
168 p := pr.p
169 br := p.mr.bufReader
170
171
172
173 for p.n == 0 && p.err == nil {
174 peek, _ := br.Peek(br.Buffered())
175 p.n, p.err = scanUntilBoundary(peek, p.mr.dashBoundary, p.mr.nlDashBoundary, p.total, p.readErr)
176 if p.n == 0 && p.err == nil {
177
178 _, p.readErr = br.Peek(len(peek) + 1)
179 if p.readErr == io.EOF {
180 p.readErr = io.ErrUnexpectedEOF
181 }
182 }
183 }
184
185
186 if p.n == 0 {
187 return 0, p.err
188 }
189 n := len(d)
190 if n > p.n {
191 n = p.n
192 }
193 n, _ = br.Read(d[:n])
194 p.total += int64(n)
195 p.n -= n
196 if p.n == 0 {
197 return n, p.err
198 }
199 return n, nil
200 }
201
202
203
204
205
206
207
208
209
210
211
212 func scanUntilBoundary(buf, dashBoundary, nlDashBoundary []byte, total int64, readErr error) (int, error) {
213 if total == 0 {
214
215 if bytes.HasPrefix(buf, dashBoundary) {
216 switch matchAfterPrefix(buf, dashBoundary, readErr) {
217 case -1:
218 return len(dashBoundary), nil
219 case 0:
220 return 0, nil
221 case +1:
222 return 0, io.EOF
223 }
224 }
225 if bytes.HasPrefix(dashBoundary, buf) {
226 return 0, readErr
227 }
228 }
229
230
231 if i := bytes.Index(buf, nlDashBoundary); i >= 0 {
232 switch matchAfterPrefix(buf[i:], nlDashBoundary, readErr) {
233 case -1:
234 return i + len(nlDashBoundary), nil
235 case 0:
236 return i, nil
237 case +1:
238 return i, io.EOF
239 }
240 }
241 if bytes.HasPrefix(nlDashBoundary, buf) {
242 return 0, readErr
243 }
244
245
246
247
248
249 i := bytes.LastIndexByte(buf, nlDashBoundary[0])
250 if i >= 0 && bytes.HasPrefix(nlDashBoundary, buf[i:]) {
251 return i, nil
252 }
253 return len(buf), readErr
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 func matchAfterPrefix(buf, prefix []byte, readErr error) int {
268 if len(buf) == len(prefix) {
269 if readErr != nil {
270 return +1
271 }
272 return 0
273 }
274 c := buf[len(prefix)]
275 if c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '-' {
276 return +1
277 }
278 return -1
279 }
280
281 func (p *Part) Close() error {
282 io.Copy(ioutil.Discard, p)
283 return nil
284 }
285
286
287
288
289 type Reader struct {
290 bufReader *bufio.Reader
291
292 currentPart *Part
293 partsRead int
294
295 nl []byte
296 nlDashBoundary []byte
297 dashBoundaryDash []byte
298 dashBoundary []byte
299 }
300
301
302
303 func (r *Reader) NextPart() (*Part, error) {
304 if r.currentPart != nil {
305 r.currentPart.Close()
306 }
307 if string(r.dashBoundary) == "--" {
308 return nil, fmt.Errorf("multipart: boundary is empty")
309 }
310 expectNewPart := false
311 for {
312 line, err := r.bufReader.ReadSlice('\n')
313
314 if err == io.EOF && r.isFinalBoundary(line) {
315
316
317
318
319
320 return nil, io.EOF
321 }
322 if err != nil {
323 return nil, fmt.Errorf("multipart: NextPart: %v", err)
324 }
325
326 if r.isBoundaryDelimiterLine(line) {
327 r.partsRead++
328 bp, err := newPart(r)
329 if err != nil {
330 return nil, err
331 }
332 r.currentPart = bp
333 return bp, nil
334 }
335
336 if r.isFinalBoundary(line) {
337
338 return nil, io.EOF
339 }
340
341 if expectNewPart {
342 return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
343 }
344
345 if r.partsRead == 0 {
346
347 continue
348 }
349
350
351
352
353
354 if bytes.Equal(line, r.nl) {
355 expectNewPart = true
356 continue
357 }
358
359 return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)
360 }
361 }
362
363
364
365
366 func (mr *Reader) isFinalBoundary(line []byte) bool {
367 if !bytes.HasPrefix(line, mr.dashBoundaryDash) {
368 return false
369 }
370 rest := line[len(mr.dashBoundaryDash):]
371 rest = skipLWSPChar(rest)
372 return len(rest) == 0 || bytes.Equal(rest, mr.nl)
373 }
374
375 func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
376
377
378
379
380
381
382 if !bytes.HasPrefix(line, mr.dashBoundary) {
383 return false
384 }
385 rest := line[len(mr.dashBoundary):]
386 rest = skipLWSPChar(rest)
387
388
389
390
391 if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' {
392 mr.nl = mr.nl[1:]
393 mr.nlDashBoundary = mr.nlDashBoundary[1:]
394 }
395 return bytes.Equal(rest, mr.nl)
396 }
397
398
399
400
401 func skipLWSPChar(b []byte) []byte {
402 for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
403 b = b[1:]
404 }
405 return b
406 }
407
View as plain text