89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
package stringutil
|
|
|
|
import (
|
|
"bytes"
|
|
"net/mail"
|
|
"strings"
|
|
)
|
|
|
|
// JoinAddress formats a slice of Address structs such that they can be used in a To or Cc header.
|
|
func JoinAddress(addrs []mail.Address) string {
|
|
if len(addrs) == 0 {
|
|
return ""
|
|
}
|
|
buf := &bytes.Buffer{}
|
|
for i, a := range addrs {
|
|
if i > 0 {
|
|
_, _ = buf.WriteString(", ")
|
|
}
|
|
_, _ = buf.WriteString(a.String())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// EnsureCommaDelimitedAddresses is used by AddressList to ensure that address lists are properly
|
|
// delimited.
|
|
func EnsureCommaDelimitedAddresses(s string) string {
|
|
// This normalizes the whitespace, but may interfere with CFWS (comments with folding whitespace)
|
|
// RFC-5322 3.4.0:
|
|
// because some legacy implementations interpret the comment,
|
|
// comments generally SHOULD NOT be used in address fields
|
|
// to avoid confusing such implementations.
|
|
s = strings.Join(strings.Fields(s), " ")
|
|
|
|
inQuotes := false
|
|
inDomain := false
|
|
escapeSequence := false
|
|
sb := strings.Builder{}
|
|
for i, r := range s {
|
|
if escapeSequence {
|
|
escapeSequence = false
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
if r == '"' {
|
|
inQuotes = !inQuotes
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
if inQuotes {
|
|
if r == '\\' {
|
|
escapeSequence = true
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
} else {
|
|
if r == '@' {
|
|
inDomain = true
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
if inDomain {
|
|
if r == ';' {
|
|
inDomain = false
|
|
if i == len(s)-1 {
|
|
// omit trailing semicolon
|
|
continue
|
|
}
|
|
|
|
sb.WriteRune(',')
|
|
continue
|
|
}
|
|
if r == ',' {
|
|
inDomain = false
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
if r == ' ' {
|
|
inDomain = false
|
|
sb.WriteRune(',')
|
|
sb.WriteRune(r)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
sb.WriteRune(r)
|
|
}
|
|
return sb.String()
|
|
}
|