// qDecode decodes a Q encoded string. func qDecode(s string) ([]byte, error) { length := len(s) for i := 0; i < len(s); i++ { if s[i] == '=' { length -= 2 i += 2 } } dec := make([]byte, length) n := 0 for i := 0; i < len(s); i++ { switch c := s[i]; { case c == '_': dec[n] = ' ' case c == '=': if i+2 >= len(s) { return []byte(""), io.ErrUnexpectedEOF } buf, err := internal.ReadHexByte([]byte(s[i+1:])) if err != nil { return []byte(""), err } dec[n] = buf i += 2 case (c >= ' ' && c <= '~') || c == '\n' || c == '\r' || c == '\t': dec[n] = c default: return []byte(""), fmt.Errorf("quotedprintable: invalid unescaped byte %#02x in Q encoded string at byte %d", c, i) } n++ } return dec, nil }
// Decode decodes src into at most MaxDecodedLen(len(src)) bytes to dst, // returning the actual number of bytes written to dst. func Decode(dst, src []byte) (n int, err error) { var eol, trimLen, eolLen int for i := 0; i < len(src); i++ { if i == eol { eol = bytes.IndexByte(src[i:], '\n') + i + 1 if eol == i { eol = len(src) eolLen = 0 } else if eol-2 >= i && src[eol-2] == '\r' { eolLen = 2 } else { eolLen = 1 } // Count the number of bytes to trim trimLen = 0 for { if trimLen == eol-eolLen-i { break } switch src[eol-eolLen-trimLen-1] { case '\n', '\r', ' ', '\t': trimLen++ continue case '=': if trimLen > 0 { trimLen += eolLen + 1 eolLen = 0 err = fmt.Errorf("quotedprintable: invalid bytes after =: %q", src[eol-trimLen+1:eol]) } else { trimLen = eolLen + 1 eolLen = 0 } } break } } // Skip trimmable bytes if trimLen > 0 && i == eol-trimLen-eolLen { if err != nil { return n, err } i += trimLen - 1 continue } switch c := src[i]; { case c == '=': if i+2 >= len(src) { return n, io.ErrUnexpectedEOF } b, convErr := internal.ReadHexByte(src[i+1:]) if convErr != nil { return n, convErr } dst[n] = b n++ i += 2 case (c >= ' ' && c <= '~') || c == '\n' || c == '\r' || c == '\t': dst[n] = c n++ default: return n, fmt.Errorf("quotedprintable: invalid unescaped byte 0x%02x in quoted-printable body", c) } } return n, nil }