Copyright 2010 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 mime

import (
	
	
	
	
	
)
FormatMediaType serializes mediatype t and the parameters param as a media type conforming to RFC 2045 and RFC 2616. The type and parameter names are written in lower-case. When any of the arguments result in a standard violation then FormatMediaType returns the empty string.
func ( string,  map[string]string) string {
	var  strings.Builder
	if  := strings.IndexByte(, '/');  == -1 {
		if !isToken() {
			return ""
		}
		.WriteString(strings.ToLower())
	} else {
		,  := [:], [+1:]
		if !isToken() || !isToken() {
			return ""
		}
		.WriteString(strings.ToLower())
		.WriteByte('/')
		.WriteString(strings.ToLower())
	}

	 := make([]string, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Strings()

	for ,  := range  {
		 := []
		.WriteByte(';')
		.WriteByte(' ')
		if !isToken() {
			return ""
		}
		.WriteString(strings.ToLower())

		 := needsEncoding()
RFC 2231 section 4
			.WriteByte('*')
		}
		.WriteByte('=')

		if  {
			.WriteString("utf-8''")

			 := 0
			for  := 0;  < len(); ++ {
{RFC 2231 section 7} attribute-char := <any (US-ASCII) CHAR except SPACE, CTLs, "*", "'", "%", or tspecials>
				if  <= ' ' ||  >= 0x7F ||
					 == '*' ||  == '\'' ||  == '%' ||
					isTSpecial(rune()) {

					.WriteString([:])
					 =  + 1

					.WriteByte('%')
					.WriteByte(upperhex[>>4])
					.WriteByte(upperhex[&0x0F])
				}
			}
			.WriteString([:])
			continue
		}

		if isToken() {
			.WriteString()
			continue
		}

		.WriteByte('"')
		 := 0
		for  := 0;  < len(); ++ {
			 := []
			if  == '"' ||  == '\\' {
				.WriteString([:])
				 = 
				.WriteByte('\\')
			}
		}
		.WriteString([:])
		.WriteByte('"')
	}
	return .String()
}

func ( string) error {
	,  := consumeToken()
	if  == "" {
		return errors.New("mime: no media type")
	}
	if  == "" {
		return nil
	}
	if !strings.HasPrefix(, "/") {
		return errors.New("mime: expected slash after first token")
	}
	,  := consumeToken([1:])
	if  == "" {
		return errors.New("mime: expected token after slash")
	}
	if  != "" {
		return errors.New("mime: unexpected content after media subtype")
	}
	return nil
}
ErrInvalidMediaParameter is returned by ParseMediaType if the media type value was found but there was an error parsing the optional parameters
var ErrInvalidMediaParameter = errors.New("mime: invalid media parameter")
ParseMediaType parses a media type value and any optional parameters, per RFC 1521. Media types are the values in Content-Type and Content-Disposition headers (RFC 2183). On success, ParseMediaType returns the media type converted to lowercase and trimmed of white space and a non-nil map. If there is an error parsing the optional parameter, the media type will be returned along with the error ErrInvalidMediaParameter. The returned map, params, maps from the lowercase attribute to the attribute value with its case preserved.
func ( string) ( string,  map[string]string,  error) {
	 := strings.Index(, ";")
	if  == -1 {
		 = len()
	}
	 = strings.TrimSpace(strings.ToLower([0:]))

	 = checkMediaTypeDisposition()
	if  != nil {
		return "", nil, 
	}

	 = make(map[string]string)
Map of base parameter name -> parameter name -> value for parameters containing a '*' character. Lazily initialized.
	var  map[string]map[string]string

	 = [:]
	for len() > 0 {
		 = strings.TrimLeftFunc(, unicode.IsSpace)
		if len() == 0 {
			break
		}
		, ,  := consumeMediaParam()
		if  == "" {
Ignore trailing semicolons. Not an error.
				return
Parse error.
			return , nil, ErrInvalidMediaParameter
		}

		 := 
		if  := strings.Index(, "*");  != -1 {
			 := [:]
			if  == nil {
				 = make(map[string]map[string]string)
			}
			var  bool
			if ,  = []; ! {
				[] = make(map[string]string)
				 = []
			}
		}
Duplicate parameter name is bogus.
			return "", nil, errors.New("mime: duplicate parameter name")
		}
		[] = 
		 = 
	}
Stitch together any continuations or things with stars (i.e. RFC 2231 things with stars: "foo*0" or "foo*")
	var  strings.Builder
	for ,  := range  {
		 :=  + "*"
		if ,  := [];  {
			if ,  := decode2231Enc();  {
				[] = 
			}
			continue
		}

		.Reset()
		 := false
		for  := 0; ; ++ {
			 := fmt.Sprintf("%s*%d", , )
			if ,  := [];  {
				 = true
				.WriteString()
				continue
			}
			 :=  + "*"
			,  := []
			if ! {
				break
			}
			 = true
			if  == 0 {
				if ,  := decode2231Enc();  {
					.WriteString()
				}
			} else {
				,  := percentHexUnescape()
				.WriteString()
			}
		}
		if  {
			[] = .String()
		}
	}

	return
}

func ( string) (string, bool) {
	 := strings.SplitN(, "'", 3)
	if len() != 3 {
		return "", false
TODO: ignoring lang in sv[1] for now. If anybody needs it we'll need to decide how to expose it in the API. But I'm not sure anybody uses it in practice.
	 := strings.ToLower([0])
	if len() == 0 {
		return "", false
	}
TODO: unsupported encoding
		return "", false
	}
	,  := percentHexUnescape([2])
	if  != nil {
		return "", false
	}
	return , true
}

func ( rune) bool {
	return !isTokenChar()
}
consumeToken consumes a token from the beginning of provided string, per RFC 2045 section 5.1 (referenced from 2183), and return the token consumed and the rest of the string. Returns ("", v) on failure to consume at least one character.
func ( string) (,  string) {
	 := strings.IndexFunc(, isNotTokenChar)
	if  == -1 {
		return , ""
	}
	if  == 0 {
		return "", 
	}
	return [0:], [:]
}
consumeValue consumes a "value" per RFC 2045, where a value is either a 'token' or a 'quoted-string'. On success, consumeValue returns the value consumed (and de-quoted/escaped, if a quoted-string) and the rest of the string. On failure, returns ("", v).
func ( string) (,  string) {
	if  == "" {
		return
	}
	if [0] != '"' {
		return consumeToken()
	}
parse a quoted-string
	 := new(strings.Builder)
	for  := 1;  < len(); ++ {
		 := []
		if  == '"' {
			return .String(), [+1:]
When MSIE sends a full file path (in "intranet mode"), it does not escape backslashes: "C:\dev\go\foo.txt", not "C:\\dev\\go\\foo.txt". No known MIME generators emit unnecessary backslash escapes for simple token characters like numbers and letters. If we see an unnecessary backslash escape, assume it is from MSIE and intended as a literal backslash. This makes Go servers deal better with MSIE without affecting the way they handle conforming MIME generators.
		if  == '\\' && +1 < len() && isTSpecial(rune([+1])) {
			.WriteByte([+1])
			++
			continue
		}
		if  == '\r' ||  == '\n' {
			return "", 
		}
		.WriteByte([])
Did not find end quote.
	return "", 
}

func ( string) (, ,  string) {
	 = strings.TrimLeftFunc(, unicode.IsSpace)
	if !strings.HasPrefix(, ";") {
		return "", "", 
	}

	 = [1:] // consume semicolon
	 = strings.TrimLeftFunc(, unicode.IsSpace)
	,  = consumeToken()
	 = strings.ToLower()
	if  == "" {
		return "", "", 
	}

	 = strings.TrimLeftFunc(, unicode.IsSpace)
	if !strings.HasPrefix(, "=") {
		return "", "", 
	}
	 = [1:] // consume equals sign
	 = strings.TrimLeftFunc(, unicode.IsSpace)
	,  := consumeValue()
	if  == "" &&  ==  {
		return "", "", 
	}
	 = 
	return , , 
}

Count %, check that they're well-formed.
	 := 0
	for  := 0;  < len(); {
		if [] != '%' {
			++
			continue
		}
		++
		if +2 >= len() || !ishex([+1]) || !ishex([+2]) {
			 = [:]
			if len() > 3 {
				 = [0:3]
			}
			return "", fmt.Errorf("mime: bogus characters after %%: %q", )
		}
		 += 3
	}
	if  == 0 {
		return , nil
	}

	 := make([]byte, len()-2*)
	 := 0
	for  := 0;  < len(); {
		switch [] {
		case '%':
			[] = unhex([+1])<<4 | unhex([+2])
			++
			 += 3
		default:
			[] = []
			++
			++
		}
	}
	return string(), nil
}

func ( byte) bool {
	switch {
	case '0' <=  &&  <= '9':
		return true
	case 'a' <=  &&  <= 'f':
		return true
	case 'A' <=  &&  <= 'F':
		return true
	}
	return false
}

func ( byte) byte {
	switch {
	case '0' <=  &&  <= '9':
		return  - '0'
	case 'a' <=  &&  <= 'f':
		return  - 'a' + 10
	case 'A' <=  &&  <= 'F':
		return  - 'A' + 10
	}
	return 0