Source file src/pkg/mime/multipart/multipart.go
1 // Copyright 2010 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
6 /*
7 Package multipart implements MIME multipart parsing, as defined in RFC
8 2046.
9
10 The implementation is sufficient for HTTP (RFC 2388) and the multipart
11 bodies generated by popular browsers.
12 */
13 package multipart
14
15 import (
16 "bufio"
17 "bytes"
18 "fmt"
19 "io"
20 "io/ioutil"
21 "mime"
22 "net/textproto"
23 )
24
25 // TODO(bradfitz): inline these once the compiler can inline them in
26 // read-only situation (such as bytes.HasSuffix)
27 var lf = []byte("\n")
28 var crlf = []byte("\r\n")
29
30 var emptyParams = make(map[string]string)
31
32 // A Part represents a single part in a multipart body.
33 type Part struct {
34 // The headers of the body, if any, with the keys canonicalized
35 // in the same fashion that the Go http.Request headers are.
36 // i.e. "foo-bar" changes case to "Foo-Bar"
37 Header textproto.MIMEHeader
38
39 buffer *bytes.Buffer
40 mr *Reader
41
42 disposition string
43 dispositionParams map[string]string
44 }
45
46 // FormName returns the name parameter if p has a Content-Disposition
47 // of type "form-data". Otherwise it returns the empty string.
48 func (p *Part) FormName() string {
49 // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF
50 // of Content-Disposition value format.
51 if p.dispositionParams == nil {
52 p.parseContentDisposition()
53 }
54 if p.disposition != "form-data" {
55 return ""
56 }
57 return p.dispositionParams["name"]
58 }
59
60 // FileName returns the filename parameter of the Part's
61 // Content-Disposition header.
62 func (p *Part) FileName() string {
63 if p.dispositionParams == nil {
64 p.parseContentDisposition()
65 }
66 return p.dispositionParams["filename"]
67 }
68
69 func (p *Part) parseContentDisposition() {
70 v := p.Header.Get("Content-Disposition")
71 var err error
72 p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
73 if err != nil {
74 p.dispositionParams = emptyParams
75 }
76 }
77
78 // NewReader creates a new multipart Reader reading from r using the
79 // given MIME boundary.
80 func NewReader(reader io.Reader, boundary string) *Reader {
81 b := []byte("\r\n--" + boundary + "--")
82 return &Reader{
83 bufReader: bufio.NewReader(reader),
84
85 nl: b[:2],
86 nlDashBoundary: b[:len(b)-2],
87 dashBoundaryDash: b[2:],
88 dashBoundary: b[2 : len(b)-2],
89 }
90 }
91
92 func newPart(mr *Reader) (*Part, error) {
93 bp := &Part{
94 Header: make(map[string][]string),
95 mr: mr,
96 buffer: new(bytes.Buffer),
97 }
98 if err := bp.populateHeaders(); err != nil {
99 return nil, err
100 }
101 return bp, nil
102 }
103
104 func (bp *Part) populateHeaders() error {
105 r := textproto.NewReader(bp.mr.bufReader)
106 header, err := r.ReadMIMEHeader()
107 if err == nil {
108 bp.Header = header
109 }
110 return err
111 }
112
113 // Read reads the body of a part, after its headers and before the
114 // next part (if any) begins.
115 func (p *Part) Read(d []byte) (n int, err error) {
116 if p.buffer.Len() >= len(d) {
117 // Internal buffer of unconsumed data is large enough for
118 // the read request. No need to parse more at the moment.
119 return p.buffer.Read(d)
120 }
121 peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
122 unexpectedEof := err == io.EOF
123 if err != nil && !unexpectedEof {
124 return 0, fmt.Errorf("multipart: Part Read: %v", err)
125 }
126 if peek == nil {
127 panic("nil peek buf")
128 }
129
130 // Search the peek buffer for "\r\n--boundary". If found,
131 // consume everything up to the boundary. If not, consume only
132 // as much of the peek buffer as cannot hold the boundary
133 // string.
134 nCopy := 0
135 foundBoundary := false
136 if idx := bytes.Index(peek, p.mr.nlDashBoundary); idx != -1 {
137 nCopy = idx
138 foundBoundary = true
139 } else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
140 nCopy = safeCount
141 } else if unexpectedEof {
142 // If we've run out of peek buffer and the boundary
143 // wasn't found (and can't possibly fit), we must have
144 // hit the end of the file unexpectedly.
145 return 0, io.ErrUnexpectedEOF
146 }
147 if nCopy > 0 {
148 if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
149 return 0, err
150 }
151 }
152 n, err = p.buffer.Read(d)
153 if err == io.EOF && !foundBoundary {
154 // If the boundary hasn't been reached there's more to
155 // read, so don't pass through an EOF from the buffer
156 err = nil
157 }
158 return
159 }
160
161 func (p *Part) Close() error {
162 io.Copy(ioutil.Discard, p)
163 return nil
164 }
165
166 // Reader is an iterator over parts in a MIME multipart body.
167 // Reader's underlying parser consumes its input as needed. Seeking
168 // isn't supported.
169 type Reader struct {
170 bufReader *bufio.Reader
171
172 currentPart *Part
173 partsRead int
174
175 nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
176 }
177
178 // NextPart returns the next part in the multipart or an error.
179 // When there are no more parts, the error io.EOF is returned.
180 func (r *Reader) NextPart() (*Part, error) {
181 if r.currentPart != nil {
182 r.currentPart.Close()
183 }
184
185 expectNewPart := false
186 for {
187 line, err := r.bufReader.ReadSlice('\n')
188 if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
189 // If the buffer ends in "--boundary--" without the
190 // trailing "\r\n", ReadSlice will return an error
191 // (since it's missing the '\n'), but this is a valid
192 // multipart EOF so we need to return io.EOF instead of
193 // a fmt-wrapped one.
194 return nil, io.EOF
195 }
196 if err != nil {
197 return nil, fmt.Errorf("multipart: NextPart: %v", err)
198 }
199
200 if r.isBoundaryDelimiterLine(line) {
201 r.partsRead++
202 bp, err := newPart(r)
203 if err != nil {
204 return nil, err
205 }
206 r.currentPart = bp
207 return bp, nil
208 }
209
210 if hasPrefixThenNewline(line, r.dashBoundaryDash) {
211 // Expected EOF
212 return nil, io.EOF
213 }
214
215 if expectNewPart {
216 return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
217 }
218
219 if r.partsRead == 0 {
220 // skip line
221 continue
222 }
223
224 // Consume the "\n" or "\r\n" separator between the
225 // body of the previous part and the boundary line we
226 // now expect will follow. (either a new part or the
227 // end boundary)
228 if bytes.Equal(line, r.nl) {
229 expectNewPart = true
230 continue
231 }
232
233 return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)
234 }
235 panic("unreachable")
236 }
237
238 func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
239 // http://tools.ietf.org/html/rfc2046#section-5.1
240 // The boundary delimiter line is then defined as a line
241 // consisting entirely of two hyphen characters ("-",
242 // decimal value 45) followed by the boundary parameter
243 // value from the Content-Type header field, optional linear
244 // whitespace, and a terminating CRLF.
245 if !bytes.HasPrefix(line, mr.dashBoundary) {
246 return false
247 }
248 if bytes.HasSuffix(line, mr.nl) {
249 return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
250 }
251 // Violate the spec and also support newlines without the
252 // carriage return...
253 if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
254 if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
255 mr.nl = mr.nl[1:]
256 mr.nlDashBoundary = mr.nlDashBoundary[1:]
257 return true
258 }
259 }
260 return false
261 }
262
263 func onlyHorizontalWhitespace(s []byte) bool {
264 for _, b := range s {
265 if b != ' ' && b != '\t' {
266 return false
267 }
268 }
269 return true
270 }
271
272 func hasPrefixThenNewline(s, prefix []byte) bool {
273 return bytes.HasPrefix(s, prefix) &&
274 (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
275 len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
276 }