github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/mime/multipart/writer.go (about) 1 // Copyright 2011 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 package multipart 6 7 import ( 8 "bytes" 9 "crypto/rand" 10 "errors" 11 "fmt" 12 "io" 13 "net/textproto" 14 "strings" 15 ) 16 17 // A Writer generates multipart messages. 18 type Writer struct { 19 w io.Writer 20 boundary string 21 lastpart *part 22 } 23 24 // NewWriter returns a new multipart Writer with a random boundary, 25 // writing to w. 26 func NewWriter(w io.Writer) *Writer { 27 return &Writer{ 28 w: w, 29 boundary: randomBoundary(), 30 } 31 } 32 33 // Boundary returns the Writer's boundary. 34 func (w *Writer) Boundary() string { 35 return w.boundary 36 } 37 38 // SetBoundary overrides the Writer's default randomly-generated 39 // boundary separator with an explicit value. 40 // 41 // SetBoundary must be called before any parts are created, may only 42 // contain certain ASCII characters, and must be 1-69 bytes long. 43 func (w *Writer) SetBoundary(boundary string) error { 44 if w.lastpart != nil { 45 return errors.New("mime: SetBoundary called after write") 46 } 47 // rfc2046#section-5.1.1 48 if len(boundary) < 1 || len(boundary) > 69 { 49 return errors.New("mime: invalid boundary length") 50 } 51 for _, b := range boundary { 52 if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' { 53 continue 54 } 55 switch b { 56 case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?': 57 continue 58 } 59 return errors.New("mime: invalid boundary character") 60 } 61 w.boundary = boundary 62 return nil 63 } 64 65 // FormDataContentType returns the Content-Type for an HTTP 66 // multipart/form-data with this Writer's Boundary. 67 func (w *Writer) FormDataContentType() string { 68 return "multipart/form-data; boundary=" + w.boundary 69 } 70 71 func randomBoundary() string { 72 var buf [30]byte 73 _, err := io.ReadFull(rand.Reader, buf[:]) 74 if err != nil { 75 panic(err) 76 } 77 return fmt.Sprintf("%x", buf[:]) 78 } 79 80 // CreatePart creates a new multipart section with the provided 81 // header. The body of the part should be written to the returned 82 // Writer. After calling CreatePart, any previous part may no longer 83 // be written to. 84 func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) { 85 if w.lastpart != nil { 86 if err := w.lastpart.close(); err != nil { 87 return nil, err 88 } 89 } 90 var b bytes.Buffer 91 if w.lastpart != nil { 92 fmt.Fprintf(&b, "\r\n--%s\r\n", w.boundary) 93 } else { 94 fmt.Fprintf(&b, "--%s\r\n", w.boundary) 95 } 96 // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort 97 // and clean, like http.Header.Write(w) does. 98 for k, vv := range header { 99 for _, v := range vv { 100 fmt.Fprintf(&b, "%s: %s\r\n", k, v) 101 } 102 } 103 fmt.Fprintf(&b, "\r\n") 104 _, err := io.Copy(w.w, &b) 105 if err != nil { 106 return nil, err 107 } 108 p := &part{ 109 mw: w, 110 } 111 w.lastpart = p 112 return p, nil 113 } 114 115 var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") 116 117 func escapeQuotes(s string) string { 118 return quoteEscaper.Replace(s) 119 } 120 121 // CreateFormFile is a convenience wrapper around CreatePart. It creates 122 // a new form-data header with the provided field name and file name. 123 func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error) { 124 h := make(textproto.MIMEHeader) 125 h.Set("Content-Disposition", 126 fmt.Sprintf(`form-data; name="%s"; filename="%s"`, 127 escapeQuotes(fieldname), escapeQuotes(filename))) 128 h.Set("Content-Type", "application/octet-stream") 129 return w.CreatePart(h) 130 } 131 132 // CreateFormField calls CreatePart with a header using the 133 // given field name. 134 func (w *Writer) CreateFormField(fieldname string) (io.Writer, error) { 135 h := make(textproto.MIMEHeader) 136 h.Set("Content-Disposition", 137 fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname))) 138 return w.CreatePart(h) 139 } 140 141 // WriteField calls CreateFormField and then writes the given value. 142 func (w *Writer) WriteField(fieldname, value string) error { 143 p, err := w.CreateFormField(fieldname) 144 if err != nil { 145 return err 146 } 147 _, err = p.Write([]byte(value)) 148 return err 149 } 150 151 // Close finishes the multipart message and writes the trailing 152 // boundary end line to the output. 153 func (w *Writer) Close() error { 154 if w.lastpart != nil { 155 if err := w.lastpart.close(); err != nil { 156 return err 157 } 158 w.lastpart = nil 159 } 160 _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.boundary) 161 return err 162 } 163 164 type part struct { 165 mw *Writer 166 closed bool 167 we error // last error that occurred writing 168 } 169 170 func (p *part) close() error { 171 p.closed = true 172 return p.we 173 } 174 175 func (p *part) Write(d []byte) (n int, err error) { 176 if p.closed { 177 return 0, errors.New("multipart: can't write to finished part") 178 } 179 n, err = p.mw.w.Write(d) 180 if err != nil { 181 p.we = err 182 } 183 return 184 }