aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-imap/utf7
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-imap/utf7')
-rw-r--r--vendor/github.com/emersion/go-imap/utf7/decoder.go149
-rw-r--r--vendor/github.com/emersion/go-imap/utf7/encoder.go91
-rw-r--r--vendor/github.com/emersion/go-imap/utf7/utf7.go34
3 files changed, 274 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/utf7/decoder.go b/vendor/github.com/emersion/go-imap/utf7/decoder.go
new file mode 100644
index 0000000..cfcba8c
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/utf7/decoder.go
@@ -0,0 +1,149 @@
+package utf7
+
+import (
+ "errors"
+ "unicode/utf16"
+ "unicode/utf8"
+
+ "golang.org/x/text/transform"
+)
+
+// ErrInvalidUTF7 means that a transformer encountered invalid UTF-7.
+var ErrInvalidUTF7 = errors.New("utf7: invalid UTF-7")
+
+type decoder struct {
+ ascii bool
+}
+
+func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ for i := 0; i < len(src); i++ {
+ ch := src[i]
+
+ if ch < min || ch > max { // Illegal code point in ASCII mode
+ err = ErrInvalidUTF7
+ return
+ }
+
+ if ch != '&' {
+ if nDst+1 > len(dst) {
+ err = transform.ErrShortDst
+ return
+ }
+
+ nSrc++
+
+ dst[nDst] = ch
+ nDst++
+
+ d.ascii = true
+ continue
+ }
+
+ // Find the end of the Base64 or "&-" segment
+ start := i + 1
+ for i++; i < len(src) && src[i] != '-'; i++ {
+ if src[i] == '\r' || src[i] == '\n' { // base64 package ignores CR and LF
+ err = ErrInvalidUTF7
+ return
+ }
+ }
+
+ if i == len(src) { // Implicit shift ("&...")
+ if atEOF {
+ err = ErrInvalidUTF7
+ } else {
+ err = transform.ErrShortSrc
+ }
+ return
+ }
+
+ var b []byte
+ if i == start { // Escape sequence "&-"
+ b = []byte{'&'}
+ d.ascii = true
+ } else { // Control or non-ASCII code points in base64
+ if !d.ascii { // Null shift ("&...-&...-")
+ err = ErrInvalidUTF7
+ return
+ }
+
+ b = decode(src[start:i])
+ d.ascii = false
+ }
+
+ if len(b) == 0 { // Bad encoding
+ err = ErrInvalidUTF7
+ return
+ }
+
+ if nDst+len(b) > len(dst) {
+ d.ascii = true
+ err = transform.ErrShortDst
+ return
+ }
+
+ nSrc = i + 1
+
+ for _, ch := range b {
+ dst[nDst] = ch
+ nDst++
+ }
+ }
+
+ if atEOF {
+ d.ascii = true
+ }
+
+ return
+}
+
+func (d *decoder) Reset() {
+ d.ascii = true
+}
+
+// Extracts UTF-16-BE bytes from base64 data and converts them to UTF-8.
+// A nil slice is returned if the encoding is invalid.
+func decode(b64 []byte) []byte {
+ var b []byte
+
+ // Allocate a single block of memory large enough to store the Base64 data
+ // (if padding is required), UTF-16-BE bytes, and decoded UTF-8 bytes.
+ // Since a 2-byte UTF-16 sequence may expand into a 3-byte UTF-8 sequence,
+ // double the space allocation for UTF-8.
+ if n := len(b64); b64[n-1] == '=' {
+ return nil
+ } else if n&3 == 0 {
+ b = make([]byte, b64Enc.DecodedLen(n)*3)
+ } else {
+ n += 4 - n&3
+ b = make([]byte, n+b64Enc.DecodedLen(n)*3)
+ copy(b[copy(b, b64):n], []byte("=="))
+ b64, b = b[:n], b[n:]
+ }
+
+ // Decode Base64 into the first 1/3rd of b
+ n, err := b64Enc.Decode(b, b64)
+ if err != nil || n&1 == 1 {
+ return nil
+ }
+
+ // Decode UTF-16-BE into the remaining 2/3rds of b
+ b, s := b[:n], b[n:]
+ j := 0
+ for i := 0; i < n; i += 2 {
+ r := rune(b[i])<<8 | rune(b[i+1])
+ if utf16.IsSurrogate(r) {
+ if i += 2; i == n {
+ return nil
+ }
+ r2 := rune(b[i])<<8 | rune(b[i+1])
+ if r = utf16.DecodeRune(r, r2); r == repl {
+ return nil
+ }
+ } else if min <= r && r <= max {
+ return nil
+ }
+ j += utf8.EncodeRune(s[j:], r)
+ }
+ return s[:j]
+}
diff --git a/vendor/github.com/emersion/go-imap/utf7/encoder.go b/vendor/github.com/emersion/go-imap/utf7/encoder.go
new file mode 100644
index 0000000..8414d10
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/utf7/encoder.go
@@ -0,0 +1,91 @@
+package utf7
+
+import (
+ "unicode/utf16"
+ "unicode/utf8"
+
+ "golang.org/x/text/transform"
+)
+
+type encoder struct{}
+
+func (e *encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+ for i := 0; i < len(src); {
+ ch := src[i]
+
+ var b []byte
+ if min <= ch && ch <= max {
+ b = []byte{ch}
+ if ch == '&' {
+ b = append(b, '-')
+ }
+
+ i++
+ } else {
+ start := i
+
+ // Find the next printable ASCII code point
+ i++
+ for i < len(src) && (src[i] < min || src[i] > max) {
+ i++
+ }
+
+ if !atEOF && i == len(src) {
+ err = transform.ErrShortSrc
+ return
+ }
+
+ b = encode(src[start:i])
+ }
+
+ if nDst+len(b) > len(dst) {
+ err = transform.ErrShortDst
+ return
+ }
+
+ nSrc = i
+
+ for _, ch := range b {
+ dst[nDst] = ch
+ nDst++
+ }
+ }
+
+ return
+}
+
+func (e *encoder) Reset() {}
+
+// Converts string s from UTF-8 to UTF-16-BE, encodes the result as base64,
+// removes the padding, and adds UTF-7 shifts.
+func encode(s []byte) []byte {
+ // len(s) is sufficient for UTF-8 to UTF-16 conversion if there are no
+ // control code points (see table below).
+ b := make([]byte, 0, len(s)+4)
+ for len(s) > 0 {
+ r, size := utf8.DecodeRune(s)
+ if r > utf8.MaxRune {
+ r, size = utf8.RuneError, 1 // Bug fix (issue 3785)
+ }
+ s = s[size:]
+ if r1, r2 := utf16.EncodeRune(r); r1 != repl {
+ b = append(b, byte(r1>>8), byte(r1))
+ r = r2
+ }
+ b = append(b, byte(r>>8), byte(r))
+ }
+
+ // Encode as base64
+ n := b64Enc.EncodedLen(len(b)) + 2
+ b64 := make([]byte, n)
+ b64Enc.Encode(b64[1:], b)
+
+ // Strip padding
+ n -= 2 - (len(b)+2)%3
+ b64 = b64[:n]
+
+ // Add UTF-7 shifts
+ b64[0] = '&'
+ b64[n-1] = '-'
+ return b64
+}
diff --git a/vendor/github.com/emersion/go-imap/utf7/utf7.go b/vendor/github.com/emersion/go-imap/utf7/utf7.go
new file mode 100644
index 0000000..b9dd962
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/utf7/utf7.go
@@ -0,0 +1,34 @@
+// Package utf7 implements modified UTF-7 encoding defined in RFC 3501 section 5.1.3
+package utf7
+
+import (
+ "encoding/base64"
+
+ "golang.org/x/text/encoding"
+)
+
+const (
+ min = 0x20 // Minimum self-representing UTF-7 value
+ max = 0x7E // Maximum self-representing UTF-7 value
+
+ repl = '\uFFFD' // Unicode replacement code point
+)
+
+var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
+
+type enc struct{}
+
+func (e enc) NewDecoder() *encoding.Decoder {
+ return &encoding.Decoder{
+ Transformer: &decoder{true},
+ }
+}
+
+func (e enc) NewEncoder() *encoding.Encoder {
+ return &encoding.Encoder{
+ Transformer: &encoder{},
+ }
+}
+
+// Encoding is the modified UTF-7 encoding.
+var Encoding encoding.Encoding = enc{}