// 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.
type Writer struct {
	w        io.Writer
	boundary string
	lastpart *part
}

// 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.
func ( *Writer) () string {
	 := .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 randomBoundary() 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)
	}

	for ,  := range slices.Sorted(maps.Keys()) {
		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 escapeQuotes( string) string {
	return quoteEscaper.Replace()
}

// CreateFormFile is a convenience wrapper around [Writer.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 [Writer.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 [Writer.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
}