src/pkg/mime/multipart/writer.go - The Go Programming Language

Golang

Source file src/pkg/mime/multipart/writer.go

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