diff --git a/vendor/future/mime/multipart/formdata.go b/vendor/future/mime/multipart/formdata.go new file mode 100644 index 000000000..eee53fc8d --- /dev/null +++ b/vendor/future/mime/multipart/formdata.go @@ -0,0 +1,157 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package multipart + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "net/textproto" + "os" +) + +// TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here +// with that of the http package's ParseForm. + +// ReadForm parses an entire multipart message whose parts have +// a Content-Disposition of "form-data". +// It stores up to maxMemory bytes of the file parts in memory +// and the remainder on disk in temporary files. +func (r *Reader) ReadForm(maxMemory int64) (f *Form, err error) { + form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} + defer func() { + if err != nil { + form.RemoveAll() + } + }() + + maxValueBytes := int64(10 << 20) // 10 MB is a lot of text. + for { + p, err := r.NextPart() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + + name := p.FormName() + if name == "" { + continue + } + filename := p.FileName() + + var b bytes.Buffer + + if filename == "" { + // value, store as string in memory + n, err := io.CopyN(&b, p, maxValueBytes) + if err != nil && err != io.EOF { + return nil, err + } + maxValueBytes -= n + if maxValueBytes == 0 { + return nil, errors.New("multipart: message too large") + } + form.Value[name] = append(form.Value[name], b.String()) + continue + } + + // file, store in memory or on disk + fh := &FileHeader{ + Filename: filename, + Header: p.Header, + } + n, err := io.CopyN(&b, p, maxMemory+1) + if err != nil && err != io.EOF { + return nil, err + } + if n > maxMemory { + // too big, write to disk and flush buffer + file, err := ioutil.TempFile("", "multipart-") + if err != nil { + return nil, err + } + defer file.Close() + _, err = io.Copy(file, io.MultiReader(&b, p)) + if err != nil { + os.Remove(file.Name()) + return nil, err + } + fh.tmpfile = file.Name() + } else { + fh.content = b.Bytes() + maxMemory -= n + } + form.File[name] = append(form.File[name], fh) + } + + return form, nil +} + +// Form is a parsed multipart form. +// Its File parts are stored either in memory or on disk, +// and are accessible via the *FileHeader's Open method. +// Its Value parts are stored as strings. +// Both are keyed by field name. +type Form struct { + Value map[string][]string + File map[string][]*FileHeader +} + +// RemoveAll removes any temporary files associated with a Form. +func (f *Form) RemoveAll() error { + var err error + for _, fhs := range f.File { + for _, fh := range fhs { + if fh.tmpfile != "" { + e := os.Remove(fh.tmpfile) + if e != nil && err == nil { + err = e + } + } + } + } + return err +} + +// A FileHeader describes a file part of a multipart request. +type FileHeader struct { + Filename string + Header textproto.MIMEHeader + + content []byte + tmpfile string +} + +// Open opens and returns the FileHeader's associated File. +func (fh *FileHeader) Open() (File, error) { + if b := fh.content; b != nil { + r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) + return sectionReadCloser{r}, nil + } + return os.Open(fh.tmpfile) +} + +// File is an interface to access the file part of a multipart message. +// Its contents may be either stored in memory or on disk. +// If stored on disk, the File's underlying concrete type will be an *os.File. +type File interface { + io.Reader + io.ReaderAt + io.Seeker + io.Closer +} + +// helper types to turn a []byte into a File + +type sectionReadCloser struct { + *io.SectionReader +} + +func (rc sectionReadCloser) Close() error { + return nil +} diff --git a/vendor/future/mime/multipart/multipart.go b/vendor/future/mime/multipart/multipart.go new file mode 100644 index 000000000..e4a0dcf5e --- /dev/null +++ b/vendor/future/mime/multipart/multipart.go @@ -0,0 +1,390 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +/* +Package multipart implements MIME multipart parsing, as defined in RFC +2046. + +The implementation is sufficient for HTTP (RFC 2388) and the multipart +bodies generated by popular browsers. +*/ +package multipart + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "mime" + "mime/quotedprintable" + "net/textproto" +) + +var FuckMe int + +var emptyParams = make(map[string]string) + +// This constant needs to be at least 76 for this package to work correctly. +// This is because \r\n--separator_of_len_70- would fill the buffer and it +// wouldn't be safe to consume a single byte from it. +const peekBufferSize = 4096 + +// A Part represents a single part in a multipart body. +type Part struct { + // The headers of the body, if any, with the keys canonicalized + // in the same fashion that the Go http.Request headers are. + // For example, "foo-bar" changes case to "Foo-Bar" + // + // As a special case, if the "Content-Transfer-Encoding" header + // has a value of "quoted-printable", that header is instead + // hidden from this map and the body is transparently decoded + // during Read calls. + Header textproto.MIMEHeader + + buffer *bytes.Buffer + mr *Reader + bytesRead int + + disposition string + dispositionParams map[string]string + + // r is either a reader directly reading from mr, or it's a + // wrapper around such a reader, decoding the + // Content-Transfer-Encoding + r io.Reader +} + +// FormName returns the name parameter if p has a Content-Disposition +// of type "form-data". Otherwise it returns the empty string. +func (p *Part) FormName() string { + // See http://tools.ietf.org/html/rfc2183 section 2 for EBNF + // of Content-Disposition value format. + if p.dispositionParams == nil { + p.parseContentDisposition() + } + if p.disposition != "form-data" { + return "" + } + return p.dispositionParams["name"] +} + +// FileName returns the filename parameter of the Part's +// Content-Disposition header. +func (p *Part) FileName() string { + if p.dispositionParams == nil { + p.parseContentDisposition() + } + return p.dispositionParams["filename"] +} + +func (p *Part) parseContentDisposition() { + v := p.Header.Get("Content-Disposition") + var err error + p.disposition, p.dispositionParams, err = mime.ParseMediaType(v) + if err != nil { + p.dispositionParams = emptyParams + } +} + +// NewReader creates a new multipart Reader reading from r using the +// given MIME boundary. +// +// The boundary is usually obtained from the "boundary" parameter of +// the message's "Content-Type" header. Use mime.ParseMediaType to +// parse such headers. +func NewReader(r io.Reader, boundary string) *Reader { + b := []byte("\r\n--" + boundary + "--") + return &Reader{ + bufReader: bufio.NewReaderSize(r, peekBufferSize), + nl: b[:2], + nlDashBoundary: b[:len(b)-2], + dashBoundaryDash: b[2:], + dashBoundary: b[2 : len(b)-2], + } +} + +func newPart(mr *Reader) (*Part, error) { + bp := &Part{ + Header: make(map[string][]string), + mr: mr, + buffer: new(bytes.Buffer), + } + if err := bp.populateHeaders(); err != nil { + return nil, err + } + bp.r = partReader{bp} + const cte = "Content-Transfer-Encoding" + if bp.Header.Get(cte) == "quoted-printable" { + bp.Header.Del(cte) + bp.r = quotedprintable.NewReader(bp.r) + } + return bp, nil +} + +func (bp *Part) populateHeaders() error { + r := textproto.NewReader(bp.mr.bufReader) + header, err := r.ReadMIMEHeader() + if err == nil { + bp.Header = header + } + return err +} + +// Read reads the body of a part, after its headers and before the +// next part (if any) begins. +func (p *Part) Read(d []byte) (n int, err error) { + return p.r.Read(d) +} + +// partReader implements io.Reader by reading raw bytes directly from the +// wrapped *Part, without doing any Transfer-Encoding decoding. +type partReader struct { + p *Part +} + +func (pr partReader) Read(d []byte) (n int, err error) { + p := pr.p + defer func() { + p.bytesRead += n + }() + if p.buffer.Len() >= len(d) { + // Internal buffer of unconsumed data is large enough for + // the read request. No need to parse more at the moment. + return p.buffer.Read(d) + } + peek, err := p.mr.bufReader.Peek(peekBufferSize) // TODO(bradfitz): add buffer size accessor + + // Look for an immediate empty part without a leading \r\n + // before the boundary separator. Some MIME code makes empty + // parts like this. Most browsers, however, write the \r\n + // before the subsequent boundary even for empty parts and + // won't hit this path. + if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) { + return 0, io.EOF + } + unexpectedEOF := err == io.EOF + if err != nil && !unexpectedEOF { + return 0, fmt.Errorf("multipart: Part Read: %v", err) + } + if peek == nil { + panic("nil peek buf") + } + // Search the peek buffer for "\r\n--boundary". If found, + // consume everything up to the boundary. If not, consume only + // as much of the peek buffer as cannot hold the boundary + // string. + nCopy := 0 + foundBoundary := false + if idx, isEnd := p.mr.peekBufferSeparatorIndex(peek); idx != -1 { + nCopy = idx + foundBoundary = isEnd + if !isEnd && nCopy == 0 { + nCopy = 1 // make some progress. + } + } else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 { + nCopy = safeCount + } else if unexpectedEOF { + // If we've run out of peek buffer and the boundary + // wasn't found (and can't possibly fit), we must have + // hit the end of the file unexpectedly. + return 0, io.ErrUnexpectedEOF + } + if nCopy > 0 { + if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil { + return 0, err + } + } + n, err = p.buffer.Read(d) + if err == io.EOF && !foundBoundary { + // If the boundary hasn't been reached there's more to + // read, so don't pass through an EOF from the buffer + err = nil + } + return +} + +func (p *Part) Close() error { + io.Copy(ioutil.Discard, p) + return nil +} + +// Reader is an iterator over parts in a MIME multipart body. +// Reader's underlying parser consumes its input as needed. Seeking +// isn't supported. +type Reader struct { + bufReader *bufio.Reader + + currentPart *Part + partsRead int + + nl []byte // "\r\n" or "\n" (set after seeing first boundary line) + nlDashBoundary []byte // nl + "--boundary" + dashBoundaryDash []byte // "--boundary--" + dashBoundary []byte // "--boundary" +} + +// NextPart returns the next part in the multipart or an error. +// When there are no more parts, the error io.EOF is returned. +func (r *Reader) NextPart() (*Part, error) { + if r.currentPart != nil { + r.currentPart.Close() + } + + expectNewPart := false + for { + line, err := r.bufReader.ReadSlice('\n') + + if err == io.EOF && r.isFinalBoundary(line) { + // If the buffer ends in "--boundary--" without the + // trailing "\r\n", ReadSlice will return an error + // (since it's missing the '\n'), but this is a valid + // multipart EOF so we need to return io.EOF instead of + // a fmt-wrapped one. + return nil, io.EOF + } + if err != nil { + return nil, fmt.Errorf("multipart: NextPart: %v", err) + } + + if r.isBoundaryDelimiterLine(line) { + r.partsRead++ + bp, err := newPart(r) + if err != nil { + return nil, err + } + r.currentPart = bp + return bp, nil + } + + if r.isFinalBoundary(line) { + // Expected EOF + return nil, io.EOF + } + + if expectNewPart { + return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line)) + } + + if r.partsRead == 0 { + // skip line + continue + } + + // Consume the "\n" or "\r\n" separator between the + // body of the previous part and the boundary line we + // now expect will follow. (either a new part or the + // end boundary) + if bytes.Equal(line, r.nl) { + expectNewPart = true + continue + } + + return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line) + } +} + +// isFinalBoundary reports whether line is the final boundary line +// indicating that all parts are over. +// It matches `^--boundary--[ \t]*(\r\n)?$` +func (mr *Reader) isFinalBoundary(line []byte) bool { + if !bytes.HasPrefix(line, mr.dashBoundaryDash) { + return false + } + rest := line[len(mr.dashBoundaryDash):] + rest = skipLWSPChar(rest) + return len(rest) == 0 || bytes.Equal(rest, mr.nl) +} + +func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { + // http://tools.ietf.org/html/rfc2046#section-5.1 + // The boundary delimiter line is then defined as a line + // consisting entirely of two hyphen characters ("-", + // decimal value 45) followed by the boundary parameter + // value from the Content-Type header field, optional linear + // whitespace, and a terminating CRLF. + if !bytes.HasPrefix(line, mr.dashBoundary) { + return false + } + rest := line[len(mr.dashBoundary):] + rest = skipLWSPChar(rest) + + // On the first part, see our lines are ending in \n instead of \r\n + // and switch into that mode if so. This is a violation of the spec, + // but occurs in practice. + if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { + mr.nl = mr.nl[1:] + mr.nlDashBoundary = mr.nlDashBoundary[1:] + } + return bytes.Equal(rest, mr.nl) +} + +// peekBufferIsEmptyPart reports whether the provided peek-ahead +// buffer represents an empty part. It is called only if we've not +// already read any bytes in this part and checks for the case of MIME +// software not writing the \r\n on empty parts. Some does, some +// doesn't. +// +// This checks that what follows the "--boundary" is actually the end +// ("--boundary--" with optional whitespace) or optional whitespace +// and then a newline, so we don't catch "--boundaryFAKE", in which +// case the whole line is part of the data. +func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool { + // End of parts case. + // Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)` + if bytes.HasPrefix(peek, mr.dashBoundaryDash) { + rest := peek[len(mr.dashBoundaryDash):] + rest = skipLWSPChar(rest) + return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0 + } + if !bytes.HasPrefix(peek, mr.dashBoundary) { + return false + } + // Test whether rest matches `^[ \t]*\r\n`) + rest := peek[len(mr.dashBoundary):] + rest = skipLWSPChar(rest) + return bytes.HasPrefix(rest, mr.nl) +} + +// peekBufferSeparatorIndex returns the index of mr.nlDashBoundary in +// peek and whether it is a real boundary (and not a prefix of an +// unrelated separator). To be the end, the peek buffer must contain a +// newline after the boundary or contain the ending boundary (--separator--). +func (mr *Reader) peekBufferSeparatorIndex(peek []byte) (idx int, isEnd bool) { + idx = bytes.Index(peek, mr.nlDashBoundary) + if idx == -1 { + return + } + + peek = peek[idx+len(mr.nlDashBoundary):] + if len(peek) == 0 || len(peek) == 1 && peek[0] == '-' { + return idx, false + } + if len(peek) > 1 && peek[0] == '-' && peek[1] == '-' { + return idx, true + } + peek = skipLWSPChar(peek) + // Don't have a complete line after the peek. + if bytes.IndexByte(peek, '\n') == -1 { + return idx, false + } + if len(peek) > 0 && peek[0] == '\n' { + return idx, true + } + if len(peek) > 1 && peek[0] == '\r' && peek[1] == '\n' { + return idx, true + } + return idx, false +} + +// skipLWSPChar returns b with leading spaces and tabs removed. +// RFC 822 defines: +// LWSP-char = SPACE / HTAB +func skipLWSPChar(b []byte) []byte { + for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') { + b = b[1:] + } + return b +} diff --git a/vendor/future/mime/multipart/writer.go b/vendor/future/mime/multipart/writer.go new file mode 100644 index 000000000..80960939d --- /dev/null +++ b/vendor/future/mime/multipart/writer.go @@ -0,0 +1,185 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package multipart + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + "io" + "net/textproto" + "strings" +) + +// A Writer generates multipart messages. +type Writer struct { + w io.Writer + boundary string + lastpart *part +} + +// NewWriter returns a new multipart Writer with a random boundary, +// writing to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + boundary: randomBoundary(), + } +} + +// Boundary returns the Writer's boundary. +func (w *Writer) Boundary() string { + return w.boundary +} + +// SetBoundary overrides the Writer's default randomly-generated +// boundary separator with an explicit value. +// +// SetBoundary must be called before any parts are created, may only +// contain certain ASCII characters, and must be non-empty and +// at most 69 bytes long. +func (w *Writer) SetBoundary(boundary string) error { + if w.lastpart != nil { + return errors.New("mime: SetBoundary called after write") + } + // rfc2046#section-5.1.1 + if len(boundary) < 1 || len(boundary) > 69 { + return errors.New("mime: invalid boundary length") + } + for _, b := range boundary { + if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' { + continue + } + switch b { + case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?': + continue + } + return errors.New("mime: invalid boundary character") + } + w.boundary = boundary + return nil +} + +// FormDataContentType returns the Content-Type for an HTTP +// multipart/form-data with this Writer's Boundary. +func (w *Writer) FormDataContentType() string { + return "multipart/form-data; boundary=" + w.boundary +} + +func randomBoundary() string { + var buf [30]byte + _, err := io.ReadFull(rand.Reader, buf[:]) + if err != nil { + panic(err) + } + return fmt.Sprintf("%x", buf[:]) +} + +// CreatePart creates a new multipart section with the provided +// header. The body of the part should be written to the returned +// Writer. After calling CreatePart, any previous part may no longer +// be written to. +func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) { + if w.lastpart != nil { + if err := w.lastpart.close(); err != nil { + return nil, err + } + } + var b bytes.Buffer + if w.lastpart != nil { + fmt.Fprintf(&b, "\r\n--%s\r\n", w.boundary) + } else { + fmt.Fprintf(&b, "--%s\r\n", w.boundary) + } + // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort + // and clean, like http.Header.Write(w) does. + for k, vv := range header { + for _, v := range vv { + fmt.Fprintf(&b, "%s: %s\r\n", k, v) + } + } + fmt.Fprintf(&b, "\r\n") + _, err := io.Copy(w.w, &b) + if err != nil { + return nil, err + } + p := &part{ + mw: w, + } + w.lastpart = p + return p, nil +} + +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + +func escapeQuotes(s string) string { + return quoteEscaper.Replace(s) +} + +// CreateFormFile is a convenience wrapper around CreatePart. It creates +// a new form-data header with the provided field name and file name. +func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error) { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"; filename="%s"`, + escapeQuotes(fieldname), escapeQuotes(filename))) + h.Set("Content-Type", "application/octet-stream") + return w.CreatePart(h) +} + +// CreateFormField calls CreatePart with a header using the +// given field name. +func (w *Writer) CreateFormField(fieldname string) (io.Writer, error) { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname))) + return w.CreatePart(h) +} + +// WriteField calls CreateFormField and then writes the given value. +func (w *Writer) WriteField(fieldname, value string) error { + p, err := w.CreateFormField(fieldname) + if err != nil { + return err + } + _, err = p.Write([]byte(value)) + return err +} + +// Close finishes the multipart message and writes the trailing +// boundary end line to the output. +func (w *Writer) Close() error { + if w.lastpart != nil { + if err := w.lastpart.close(); err != nil { + return err + } + w.lastpart = nil + } + _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.boundary) + return err +} + +type part struct { + mw *Writer + closed bool + we error // last error that occurred writing +} + +func (p *part) close() error { + p.closed = true + return p.we +} + +func (p *part) Write(d []byte) (n int, err error) { + if p.closed { + return 0, errors.New("multipart: can't write to finished part") + } + n, err = p.mw.w.Write(d) + if err != nil { + p.we = err + } + return +}