12// IMAP4rev1 uses a modified version of UTF-7.
16const utf7chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"
18var utf7encoding = base64.NewEncoding(utf7chars).WithPadding(base64.NoPadding)
21 errUTF7SuperfluousShift = errors.New("utf7: superfluous unshift+shift")
22 errUTF7Base64 = errors.New("utf7: bad base64")
23 errUTF7OddSized = errors.New("utf7: odd-sized data")
24 errUTF7UnneededShift = errors.New("utf7: unneeded shift")
25 errUTF7UnfinishedShift = errors.New("utf7: unfinished shift")
26 errUTF7BadSurrogate = errors.New("utf7: bad utf16 surrogates")
29func utf7decode(s string) (string, error) {
38 if lastunshift == i-1 {
39 return "", errUTF7SuperfluousShift
43 r.WriteString(string(c))
59 buf, err := utf7encoding.DecodeString(b)
61 return "", fmt.Errorf("%w: %q: %v", errUTF7Base64, b, err)
66 return "", errUTF7OddSized
69 x := make([]rune, len(buf)/2)
72 for i := 0; i < len(buf); i += 2 {
73 x[j] = rune(buf[i])<<8 | rune(buf[i+1])
75 s0 := utf16.IsSurrogate(x[j-1])
76 s1 := utf16.IsSurrogate(x[j])
78 c := utf16.DecodeRune(x[j-1], x[j])
80 return "", fmt.Errorf("%w: decoding %x %x", errUTF7BadSurrogate, x[j-1], x[j])
86 return "", fmt.Errorf("%w: not both surrogate: %x %x", errUTF7BadSurrogate, x[j-1], x[j])
95 if c < 0x20 || c > 0x7e || c == '&' {
96 r.WriteString(string(c))
99 return "", errUTF7UnneededShift
104 return "", errUTF7UnfinishedShift
106 return r.String(), nil
109func utf7encode(s string) string {
110 var r strings.Builder
113 flushcode := func() {
118 for _, c := range code {
119 high, low := utf16.EncodeRune(c)
120 if high == 0xfffd && low == 0xfffd {
121 b.WriteByte(byte(c >> 8))
122 b.WriteByte(byte(c >> 0))
124 b.WriteByte(byte(high >> 8))
125 b.WriteByte(byte(high >> 0))
126 b.WriteByte(byte(low >> 8))
127 b.WriteByte(byte(low >> 0))
130 r.WriteString("&" + utf7encoding.EncodeToString(b.Bytes()) + "-")
134 for _, c := range s {
138 } else if c >= ' ' && c < 0x7f {
140 r.WriteString(string(c))