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