package mime
import (
)
func ( string, map[string]string) string {
var strings.Builder
if , , := strings.Cut(, "/"); ! {
if !isToken() {
return ""
}
.WriteString(strings.ToLower())
} else {
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()
if {
.WriteByte('*')
}
.WriteByte('=')
if {
.WriteString("utf-8''")
:= 0
for := 0; < len(); ++ {
:= []
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
}
var ErrInvalidMediaParameter = errors.New("mime: invalid media parameter")
func ( string) ( string, map[string]string, error) {
, , := strings.Cut(, ";")
= strings.TrimSpace(strings.ToLower())
= checkMediaTypeDisposition()
if != nil {
return "", nil,
}
= make(map[string]string)
var map[string]map[string]string
= [len():]
for len() > 0 {
= strings.TrimLeftFunc(, unicode.IsSpace)
if len() == 0 {
break
}
, , := consumeMediaParam()
if == "" {
if strings.TrimSpace() == ";" {
break
}
return , nil, ErrInvalidMediaParameter
}
:=
if , , := strings.Cut(, "*"); {
if == nil {
= make(map[string]map[string]string)
}
var bool
if , = []; ! {
[] = make(map[string]string)
= []
}
}
if , := []; {
return "", nil, errors.New("mime: duplicate parameter name")
}
[] =
=
}
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
}
:= strings.ToLower([0])
if len() == 0 {
return "", false
}
if != "us-ascii" && != "utf-8" {
return "", false
}
, := percentHexUnescape([2])
if != nil {
return "", false
}
return , true
}
func ( rune) bool {
return !isTokenChar()
}
func ( string) (, string) {
:= strings.IndexFunc(, isNotTokenChar)
if == -1 {
return , ""
}
if == 0 {
return "",
}
return [0:], [:]
}
func ( string) (, string) {
if == "" {
return
}
if [0] != '"' {
return consumeToken()
}
:= new(strings.Builder)
for := 1; < len(); ++ {
:= []
if == '"' {
return .String(), [+1:]
}
if == '\\' && +1 < len() && isTSpecial(rune([+1])) {
.WriteByte([+1])
++
continue
}
if == '\r' || == '\n' {
return "",
}
.WriteByte([])
}
return "",
}
func ( string) (, , string) {
= strings.TrimLeftFunc(, unicode.IsSpace)
if !strings.HasPrefix(, ";") {
return "", "",
}
= [1:]
= strings.TrimLeftFunc(, unicode.IsSpace)
, = consumeToken()
= strings.ToLower()
if == "" {
return "", "",
}
= strings.TrimLeftFunc(, unicode.IsSpace)
if !strings.HasPrefix(, "=") {
return "", "",
}
= [1:]
= strings.TrimLeftFunc(, unicode.IsSpace)
, := consumeValue()
if == "" && == {
return "", "",
}
=
return , ,
}
func ( string) (string, error) {
:= 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
}