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 (
	
	
	
	
	
	
	
	
)
A Writer generates multipart messages.
NewWriter returns a new multipart Writer with a random boundary, writing to w.
func ( io.Writer) *Writer {
	return &Writer{
		w:        ,
		boundary: randomBoundary(),
	}
}
Boundary returns the Writer's boundary.
func ( *Writer) () string {
	return .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 70 bytes long.
func ( *Writer) ( string) error {
	if .lastpart != nil {
		return errors.New("mime: SetBoundary called after write")
rfc2046#section-5.1.1
	if len() < 1 || len() > 70 {
		return errors.New("mime: invalid boundary length")
	}
	 := len() - 1
	for ,  := range  {
		if 'A' <=  &&  <= 'Z' || 'a' <=  &&  <= 'z' || '0' <=  &&  <= '9' {
			continue
		}
		switch  {
		case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':
			continue
		case ' ':
			if  !=  {
				continue
			}
		}
		return errors.New("mime: invalid boundary character")
	}
	.boundary = 
	return nil
}
FormDataContentType returns the Content-Type for an HTTP multipart/form-data with this Writer's Boundary.
We must quote the boundary if it contains any of the tspecials characters defined by RFC 2045, or space.
	if strings.ContainsAny(, `()<>@,;:\"/[]?= `) {
		 = `"` +  + `"`
	}
	return "multipart/form-data; boundary=" + 
}

func () string {
	var  [30]byte
	,  := io.ReadFull(rand.Reader, [:])
	if  != nil {
		panic()
	}
	return fmt.Sprintf("%x", [:])
}
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 ( *Writer) ( textproto.MIMEHeader) (io.Writer, error) {
	if .lastpart != nil {
		if  := .lastpart.close();  != nil {
			return nil, 
		}
	}
	var  bytes.Buffer
	if .lastpart != nil {
		fmt.Fprintf(&, "\r\n--%s\r\n", .boundary)
	} else {
		fmt.Fprintf(&, "--%s\r\n", .boundary)
	}

	 := make([]string, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Strings()
	for ,  := range  {
		for ,  := range [] {
			fmt.Fprintf(&, "%s: %s\r\n", , )
		}
	}
	fmt.Fprintf(&, "\r\n")
	,  := io.Copy(.w, &)
	if  != nil {
		return nil, 
	}
	 := &part{
		mw: ,
	}
	.lastpart = 
	return , nil
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func ( string) string {
	return quoteEscaper.Replace()
}
CreateFormFile is a convenience wrapper around CreatePart. It creates a new form-data header with the provided field name and file name.
func ( *Writer) (,  string) (io.Writer, error) {
	 := make(textproto.MIMEHeader)
	.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(), escapeQuotes()))
	.Set("Content-Type", "application/octet-stream")
	return .CreatePart()
}
CreateFormField calls CreatePart with a header using the given field name.
func ( *Writer) ( string) (io.Writer, error) {
	 := make(textproto.MIMEHeader)
	.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"`, escapeQuotes()))
	return .CreatePart()
}
WriteField calls CreateFormField and then writes the given value.
func ( *Writer) (,  string) error {
	,  := .CreateFormField()
	if  != nil {
		return 
	}
	_,  = .Write([]byte())
	return 
}
Close finishes the multipart message and writes the trailing boundary end line to the output.
func ( *Writer) () error {
	if .lastpart != nil {
		if  := .lastpart.close();  != nil {
			return 
		}
		.lastpart = nil
	}
	,  := fmt.Fprintf(.w, "\r\n--%s--\r\n", .boundary)
	return 
}

type part struct {
	mw     *Writer
	closed bool
	we     error // last error that occurred writing
}

func ( *part) () error {
	.closed = true
	return .we
}

func ( *part) ( []byte) ( int,  error) {
	if .closed {
		return 0, errors.New("multipart: can't write to finished part")
	}
	,  = .mw.w.Write()
	if  != nil {
		.we = 
	}
	return