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 }