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