Initialize module and dependencies
This commit is contained in:
13
vendor/github.com/emersion/go-imap/v2/internal/acl.go
generated
vendored
Normal file
13
vendor/github.com/emersion/go-imap/v2/internal/acl.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap/v2"
|
||||
)
|
||||
|
||||
func FormatRights(rm imap.RightModification, rs imap.RightSet) string {
|
||||
s := ""
|
||||
if rm != imap.RightModificationReplace {
|
||||
s = string(rm)
|
||||
}
|
||||
return s + string(rs)
|
||||
}
|
||||
306
vendor/github.com/emersion/go-imap/v2/internal/imapnum/numset.go
generated
vendored
Normal file
306
vendor/github.com/emersion/go-imap/v2/internal/imapnum/numset.go
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
package imapnum
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Range represents a single seq-number or seq-range value (RFC 3501 ABNF). Values
|
||||
// may be static (e.g. "1", "2:4") or dynamic (e.g. "*", "1:*"). A seq-number is
|
||||
// represented by setting Start = Stop. Zero is used to represent "*", which is
|
||||
// safe because seq-number uses nz-number rule. The order of values is always
|
||||
// Start <= Stop, except when representing "n:*", where Start = n and Stop = 0.
|
||||
type Range struct {
|
||||
Start, Stop uint32
|
||||
}
|
||||
|
||||
// Contains returns true if the seq-number q is contained in range value s.
|
||||
// The dynamic value "*" contains only other "*" values, the dynamic range "n:*"
|
||||
// contains "*" and all numbers >= n.
|
||||
func (s Range) Contains(q uint32) bool {
|
||||
if q == 0 {
|
||||
return s.Stop == 0 // "*" is contained only in "*" and "n:*"
|
||||
}
|
||||
return s.Start != 0 && s.Start <= q && (q <= s.Stop || s.Stop == 0)
|
||||
}
|
||||
|
||||
// Less returns true if s precedes and does not contain seq-number q.
|
||||
func (s Range) Less(q uint32) bool {
|
||||
return (s.Stop < q || q == 0) && s.Stop != 0
|
||||
}
|
||||
|
||||
// Merge combines range values s and t into a single union if the two
|
||||
// intersect or one is a superset of the other. The order of s and t does not
|
||||
// matter. If the values cannot be merged, s is returned unmodified and ok is
|
||||
// set to false.
|
||||
func (s Range) Merge(t Range) (union Range, ok bool) {
|
||||
union = s
|
||||
if s == t {
|
||||
return s, true
|
||||
}
|
||||
if s.Start != 0 && t.Start != 0 {
|
||||
// s and t are any combination of "n", "n:m", or "n:*"
|
||||
if s.Start > t.Start {
|
||||
s, t = t, s
|
||||
}
|
||||
// s starts at or before t, check where it ends
|
||||
if (s.Stop >= t.Stop && t.Stop != 0) || s.Stop == 0 {
|
||||
return s, true // s is a superset of t
|
||||
}
|
||||
// s is "n" or "n:m", if m == ^uint32(0) then t is "n:*"
|
||||
if s.Stop+1 >= t.Start || s.Stop == ^uint32(0) {
|
||||
return Range{s.Start, t.Stop}, true // s intersects or touches t
|
||||
}
|
||||
return union, false
|
||||
}
|
||||
// exactly one of s and t is "*"
|
||||
if s.Start == 0 {
|
||||
if t.Stop == 0 {
|
||||
return t, true // s is "*", t is "n:*"
|
||||
}
|
||||
} else if s.Stop == 0 {
|
||||
return s, true // s is "n:*", t is "*"
|
||||
}
|
||||
return union, false
|
||||
}
|
||||
|
||||
// String returns range value s as a seq-number or seq-range string.
|
||||
func (s Range) String() string {
|
||||
if s.Start == s.Stop {
|
||||
if s.Start == 0 {
|
||||
return "*"
|
||||
}
|
||||
return strconv.FormatUint(uint64(s.Start), 10)
|
||||
}
|
||||
b := strconv.AppendUint(make([]byte, 0, 24), uint64(s.Start), 10)
|
||||
if s.Stop == 0 {
|
||||
return string(append(b, ':', '*'))
|
||||
}
|
||||
return string(strconv.AppendUint(append(b, ':'), uint64(s.Stop), 10))
|
||||
}
|
||||
|
||||
func (s Range) append(nums []uint32) (out []uint32, ok bool) {
|
||||
if s.Start == 0 || s.Stop == 0 {
|
||||
return nil, false
|
||||
}
|
||||
for n := s.Start; n <= s.Stop; n++ {
|
||||
nums = append(nums, n)
|
||||
}
|
||||
return nums, true
|
||||
}
|
||||
|
||||
// Set is used to represent a set of message sequence numbers or UIDs (see
|
||||
// sequence-set ABNF rule). The zero value is an empty set.
|
||||
type Set []Range
|
||||
|
||||
// AddNum inserts new numbers into the set. The value 0 represents "*".
|
||||
func (s *Set) AddNum(q ...uint32) {
|
||||
for _, v := range q {
|
||||
s.insert(Range{v, v})
|
||||
}
|
||||
}
|
||||
|
||||
// AddRange inserts a new range into the set.
|
||||
func (s *Set) AddRange(start, stop uint32) {
|
||||
if (stop < start && stop != 0) || start == 0 {
|
||||
s.insert(Range{stop, start})
|
||||
} else {
|
||||
s.insert(Range{start, stop})
|
||||
}
|
||||
}
|
||||
|
||||
// AddSet inserts all values from t into s.
|
||||
func (s *Set) AddSet(t Set) {
|
||||
for _, v := range t {
|
||||
s.insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic returns true if the set contains "*" or "n:*" values.
|
||||
func (s Set) Dynamic() bool {
|
||||
return len(s) > 0 && s[len(s)-1].Stop == 0
|
||||
}
|
||||
|
||||
// Contains returns true if the non-zero sequence number or UID q is contained
|
||||
// in the set. The dynamic range "n:*" contains all q >= n. It is the caller's
|
||||
// responsibility to handle the special case where q is the maximum UID in the
|
||||
// mailbox and q < n (i.e. the set cannot match UIDs against "*:n" or "*" since
|
||||
// it doesn't know what the maximum value is).
|
||||
func (s Set) Contains(q uint32) bool {
|
||||
if _, ok := s.search(q); ok {
|
||||
return q != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Nums returns a slice of all numbers contained in the set.
|
||||
func (s Set) Nums() (nums []uint32, ok bool) {
|
||||
for _, v := range s {
|
||||
nums, ok = v.append(nums)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return nums, true
|
||||
}
|
||||
|
||||
// String returns a sorted representation of all contained number values.
|
||||
func (s Set) String() string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
b := make([]byte, 0, 64)
|
||||
for _, v := range s {
|
||||
b = append(b, ',')
|
||||
if v.Start == 0 {
|
||||
b = append(b, '*')
|
||||
continue
|
||||
}
|
||||
b = strconv.AppendUint(b, uint64(v.Start), 10)
|
||||
if v.Start != v.Stop {
|
||||
if v.Stop == 0 {
|
||||
b = append(b, ':', '*')
|
||||
continue
|
||||
}
|
||||
b = strconv.AppendUint(append(b, ':'), uint64(v.Stop), 10)
|
||||
}
|
||||
}
|
||||
return string(b[1:])
|
||||
}
|
||||
|
||||
// insert adds range value v to the set.
|
||||
func (ptr *Set) insert(v Range) {
|
||||
s := *ptr
|
||||
defer func() {
|
||||
*ptr = s
|
||||
}()
|
||||
|
||||
i, _ := s.search(v.Start)
|
||||
merged := false
|
||||
if i > 0 {
|
||||
// try merging with the preceding entry (e.g. "1,4".insert(2), i == 1)
|
||||
s[i-1], merged = s[i-1].Merge(v)
|
||||
}
|
||||
if i == len(s) {
|
||||
// v was either merged with the last entry or needs to be appended
|
||||
if !merged {
|
||||
s.insertAt(i, v)
|
||||
}
|
||||
return
|
||||
} else if merged {
|
||||
i--
|
||||
} else if s[i], merged = s[i].Merge(v); !merged {
|
||||
s.insertAt(i, v) // insert in the middle (e.g. "1,5".insert(3), i == 1)
|
||||
return
|
||||
}
|
||||
// v was merged with s[i], continue trying to merge until the end
|
||||
for j := i + 1; j < len(s); j++ {
|
||||
if s[i], merged = s[i].Merge(s[j]); !merged {
|
||||
if j > i+1 {
|
||||
// cut out all entries between i and j that were merged
|
||||
s = append(s[:i+1], s[j:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// everything after s[i] was merged
|
||||
s = s[:i+1]
|
||||
}
|
||||
|
||||
// insertAt inserts a new range value v at index i, resizing s.Set as needed.
|
||||
func (ptr *Set) insertAt(i int, v Range) {
|
||||
s := *ptr
|
||||
defer func() {
|
||||
*ptr = s
|
||||
}()
|
||||
|
||||
if n := len(s); i == n {
|
||||
// insert at the end
|
||||
s = append(s, v)
|
||||
return
|
||||
} else if n < cap(s) {
|
||||
// enough space, shift everything at and after i to the right
|
||||
s = s[:n+1]
|
||||
copy(s[i+1:], s[i:])
|
||||
} else {
|
||||
// allocate new slice and copy everything, n is at least 1
|
||||
set := make([]Range, n+1, n*2)
|
||||
copy(set, s[:i])
|
||||
copy(set[i+1:], s[i:])
|
||||
s = set
|
||||
}
|
||||
s[i] = v
|
||||
}
|
||||
|
||||
// search attempts to find the index of the range set value that contains q.
|
||||
// If no values contain q, the returned index is the position where q should be
|
||||
// inserted and ok is set to false.
|
||||
func (s Set) search(q uint32) (i int, ok bool) {
|
||||
min, max := 0, len(s)-1
|
||||
for min < max {
|
||||
if mid := (min + max) >> 1; s[mid].Less(q) {
|
||||
min = mid + 1
|
||||
} else {
|
||||
max = mid
|
||||
}
|
||||
}
|
||||
if max < 0 || s[min].Less(q) {
|
||||
return len(s), false // q is the new largest value
|
||||
}
|
||||
return min, s[min].Contains(q)
|
||||
}
|
||||
|
||||
// errBadNumSet is used to report problems with the format of a number set
|
||||
// value.
|
||||
type errBadNumSet string
|
||||
|
||||
func (err errBadNumSet) Error() string {
|
||||
return fmt.Sprintf("imap: bad number set value %q", string(err))
|
||||
}
|
||||
|
||||
// parseNum parses a single seq-number value (non-zero uint32 or "*").
|
||||
func parseNum(v string) (uint32, error) {
|
||||
if n, err := strconv.ParseUint(v, 10, 32); err == nil && v[0] != '0' {
|
||||
return uint32(n), nil
|
||||
} else if v == "*" {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, errBadNumSet(v)
|
||||
}
|
||||
|
||||
// parseNumRange creates a new seq instance by parsing strings in the format
|
||||
// "n" or "n:m", where n and/or m may be "*". An error is returned for invalid
|
||||
// values.
|
||||
func parseNumRange(v string) (Range, error) {
|
||||
var (
|
||||
r Range
|
||||
err error
|
||||
)
|
||||
if sep := strings.IndexRune(v, ':'); sep < 0 {
|
||||
r.Start, err = parseNum(v)
|
||||
r.Stop = r.Start
|
||||
return r, err
|
||||
} else if r.Start, err = parseNum(v[:sep]); err == nil {
|
||||
if r.Stop, err = parseNum(v[sep+1:]); err == nil {
|
||||
if (r.Stop < r.Start && r.Stop != 0) || r.Start == 0 {
|
||||
r.Start, r.Stop = r.Stop, r.Start
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return r, errBadNumSet(v)
|
||||
}
|
||||
|
||||
// ParseSet returns a new Set after parsing the set string.
|
||||
func ParseSet(set string) (Set, error) {
|
||||
var s Set
|
||||
for _, sv := range strings.Split(set, ",") {
|
||||
r, err := parseNumRange(sv)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
s.AddRange(r.Start, r.Stop)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
654
vendor/github.com/emersion/go-imap/v2/internal/imapwire/decoder.go
generated
vendored
Normal file
654
vendor/github.com/emersion/go-imap/v2/internal/imapwire/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,654 @@
|
||||
package imapwire
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/internal/imapnum"
|
||||
"github.com/emersion/go-imap/v2/internal/utf7"
|
||||
)
|
||||
|
||||
// This limits the max list nesting depth to prevent stack overflow.
|
||||
const maxListDepth = 1000
|
||||
|
||||
// IsAtomChar returns true if ch is an ATOM-CHAR.
|
||||
func IsAtomChar(ch byte) bool {
|
||||
switch ch {
|
||||
case '(', ')', '{', ' ', '%', '*', '"', '\\', ']':
|
||||
return false
|
||||
default:
|
||||
return !unicode.IsControl(rune(ch))
|
||||
}
|
||||
}
|
||||
|
||||
// Is non-empty char
|
||||
func isAStringChar(ch byte) bool {
|
||||
return IsAtomChar(ch) || ch == ']'
|
||||
}
|
||||
|
||||
// DecoderExpectError is an error due to the Decoder.Expect family of methods.
|
||||
type DecoderExpectError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *DecoderExpectError) Error() string {
|
||||
return fmt.Sprintf("imapwire: %v", err.Message)
|
||||
}
|
||||
|
||||
// A Decoder reads IMAP data.
|
||||
//
|
||||
// There are multiple families of methods:
|
||||
//
|
||||
// - Methods directly named after IMAP grammar elements attempt to decode
|
||||
// said element, and return false if it's another element.
|
||||
// - "Expect" methods do the same, but set the decoder error (see Err) on
|
||||
// failure.
|
||||
type Decoder struct {
|
||||
// CheckBufferedLiteralFunc is called when a literal is about to be decoded
|
||||
// and needs to be fully buffered in memory.
|
||||
CheckBufferedLiteralFunc func(size int64, nonSync bool) error
|
||||
// MaxSize defines a maximum number of bytes to be read from the input.
|
||||
// Literals are ignored.
|
||||
MaxSize int64
|
||||
|
||||
r *bufio.Reader
|
||||
side ConnSide
|
||||
err error
|
||||
literal bool
|
||||
crlf bool
|
||||
listDepth int
|
||||
readBytes int64
|
||||
}
|
||||
|
||||
// NewDecoder creates a new decoder.
|
||||
func NewDecoder(r *bufio.Reader, side ConnSide) *Decoder {
|
||||
return &Decoder{r: r, side: side}
|
||||
}
|
||||
|
||||
func (dec *Decoder) mustUnreadByte() {
|
||||
if err := dec.r.UnreadByte(); err != nil {
|
||||
panic(fmt.Errorf("imapwire: failed to unread byte: %v", err))
|
||||
}
|
||||
dec.readBytes--
|
||||
}
|
||||
|
||||
// Err returns the decoder error, if any.
|
||||
func (dec *Decoder) Err() error {
|
||||
return dec.err
|
||||
}
|
||||
|
||||
func (dec *Decoder) returnErr(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if dec.err == nil {
|
||||
dec.err = err
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dec *Decoder) readByte() (byte, bool) {
|
||||
if dec.MaxSize > 0 && dec.readBytes > dec.MaxSize {
|
||||
return 0, dec.returnErr(fmt.Errorf("imapwire: max size exceeded"))
|
||||
}
|
||||
dec.crlf = false
|
||||
if dec.literal {
|
||||
return 0, dec.returnErr(fmt.Errorf("imapwire: cannot decode while a literal is open"))
|
||||
}
|
||||
b, err := dec.r.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return b, dec.returnErr(err)
|
||||
}
|
||||
dec.readBytes++
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (dec *Decoder) acceptByte(want byte) bool {
|
||||
got, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
} else if got != want {
|
||||
dec.mustUnreadByte()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EOF returns true if end-of-file is reached.
|
||||
func (dec *Decoder) EOF() bool {
|
||||
_, err := dec.r.ReadByte()
|
||||
if err == io.EOF {
|
||||
return true
|
||||
} else if err != nil {
|
||||
return dec.returnErr(err)
|
||||
}
|
||||
dec.mustUnreadByte()
|
||||
return false
|
||||
}
|
||||
|
||||
// Expect sets the decoder error if ok is false.
|
||||
func (dec *Decoder) Expect(ok bool, name string) bool {
|
||||
if !ok {
|
||||
msg := fmt.Sprintf("expected %v", name)
|
||||
if dec.r.Buffered() > 0 {
|
||||
b, _ := dec.r.Peek(1)
|
||||
msg += fmt.Sprintf(", got %q", b)
|
||||
}
|
||||
return dec.returnErr(&DecoderExpectError{Message: msg})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) SP() bool {
|
||||
if dec.acceptByte(' ') {
|
||||
// https://github.com/emersion/go-imap/issues/571
|
||||
b, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
dec.mustUnreadByte()
|
||||
return b != '\r' && b != '\n'
|
||||
}
|
||||
|
||||
// Special case: SP is optional if the next field is a parenthesized list
|
||||
b, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
dec.mustUnreadByte()
|
||||
return b == '('
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectSP() bool {
|
||||
return dec.Expect(dec.SP(), "SP")
|
||||
}
|
||||
|
||||
func (dec *Decoder) CRLF() bool {
|
||||
dec.acceptByte(' ') // https://github.com/emersion/go-imap/issues/540
|
||||
dec.acceptByte('\r') // be liberal in what we receive and accept lone LF
|
||||
if !dec.acceptByte('\n') {
|
||||
return false
|
||||
}
|
||||
dec.crlf = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectCRLF() bool {
|
||||
return dec.Expect(dec.CRLF(), "CRLF")
|
||||
}
|
||||
|
||||
func (dec *Decoder) Func(ptr *string, valid func(ch byte) bool) bool {
|
||||
var sb strings.Builder
|
||||
for {
|
||||
b, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !valid(b) {
|
||||
dec.mustUnreadByte()
|
||||
break
|
||||
}
|
||||
|
||||
sb.WriteByte(b)
|
||||
}
|
||||
if sb.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
*ptr = sb.String()
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) Atom(ptr *string) bool {
|
||||
return dec.Func(ptr, IsAtomChar)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectAtom(ptr *string) bool {
|
||||
return dec.Expect(dec.Atom(ptr), "atom")
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNIL() bool {
|
||||
var s string
|
||||
return dec.ExpectAtom(&s) && dec.Expect(s == "NIL", "NIL")
|
||||
}
|
||||
|
||||
func (dec *Decoder) Special(b byte) bool {
|
||||
return dec.acceptByte(b)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectSpecial(b byte) bool {
|
||||
return dec.Expect(dec.Special(b), fmt.Sprintf("'%v'", string(b)))
|
||||
}
|
||||
|
||||
func (dec *Decoder) Text(ptr *string) bool {
|
||||
var sb strings.Builder
|
||||
for {
|
||||
b, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
} else if b == '\r' || b == '\n' {
|
||||
dec.mustUnreadByte()
|
||||
break
|
||||
}
|
||||
sb.WriteByte(b)
|
||||
}
|
||||
if sb.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
*ptr = sb.String()
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectText(ptr *string) bool {
|
||||
return dec.Expect(dec.Text(ptr), "text")
|
||||
}
|
||||
|
||||
func (dec *Decoder) DiscardUntilByte(untilCh byte) {
|
||||
for {
|
||||
ch, ok := dec.readByte()
|
||||
if !ok {
|
||||
return
|
||||
} else if ch == untilCh {
|
||||
dec.mustUnreadByte()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) DiscardLine() {
|
||||
if dec.crlf {
|
||||
return
|
||||
}
|
||||
var text string
|
||||
dec.Text(&text)
|
||||
dec.CRLF()
|
||||
}
|
||||
|
||||
func (dec *Decoder) DiscardValue() bool {
|
||||
var s string
|
||||
if dec.String(&s) {
|
||||
return true
|
||||
}
|
||||
|
||||
isList, err := dec.List(func() error {
|
||||
if !dec.DiscardValue() {
|
||||
return dec.Err()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
} else if isList {
|
||||
return true
|
||||
}
|
||||
|
||||
if dec.Atom(&s) {
|
||||
return true
|
||||
}
|
||||
|
||||
dec.Expect(false, "value")
|
||||
return false
|
||||
}
|
||||
|
||||
func (dec *Decoder) numberStr() (s string, ok bool) {
|
||||
var sb strings.Builder
|
||||
for {
|
||||
ch, ok := dec.readByte()
|
||||
if !ok {
|
||||
return "", false
|
||||
} else if ch < '0' || ch > '9' {
|
||||
dec.mustUnreadByte()
|
||||
break
|
||||
}
|
||||
sb.WriteByte(ch)
|
||||
}
|
||||
if sb.Len() == 0 {
|
||||
return "", false
|
||||
}
|
||||
return sb.String(), true
|
||||
}
|
||||
|
||||
func (dec *Decoder) Number(ptr *uint32) bool {
|
||||
s, ok := dec.numberStr()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
v64, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return false // can happen on overflow
|
||||
}
|
||||
*ptr = uint32(v64)
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNumber(ptr *uint32) bool {
|
||||
return dec.Expect(dec.Number(ptr), "number")
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectBodyFldOctets(ptr *uint32) bool {
|
||||
// Workaround: some servers incorrectly return "-1" for the body structure
|
||||
// size. See:
|
||||
// https://github.com/emersion/go-imap/issues/534
|
||||
if dec.acceptByte('-') {
|
||||
*ptr = 0
|
||||
return dec.Expect(dec.acceptByte('1'), "-1 (body-fld-octets workaround)")
|
||||
}
|
||||
return dec.ExpectNumber(ptr)
|
||||
}
|
||||
|
||||
func (dec *Decoder) Number64(ptr *int64) bool {
|
||||
s, ok := dec.numberStr()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return false // can happen on overflow
|
||||
}
|
||||
*ptr = v
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNumber64(ptr *int64) bool {
|
||||
return dec.Expect(dec.Number64(ptr), "number64")
|
||||
}
|
||||
|
||||
func (dec *Decoder) ModSeq(ptr *uint64) bool {
|
||||
s, ok := dec.numberStr()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return false // can happen on overflow
|
||||
}
|
||||
*ptr = v
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectModSeq(ptr *uint64) bool {
|
||||
return dec.Expect(dec.ModSeq(ptr), "mod-sequence-value")
|
||||
}
|
||||
|
||||
func (dec *Decoder) Quoted(ptr *string) bool {
|
||||
if !dec.Special('"') {
|
||||
return false
|
||||
}
|
||||
var sb strings.Builder
|
||||
for {
|
||||
ch, ok := dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if ch == '"' {
|
||||
break
|
||||
}
|
||||
|
||||
if ch == '\\' {
|
||||
ch, ok = dec.readByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteByte(ch)
|
||||
}
|
||||
*ptr = sb.String()
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectAString(ptr *string) bool {
|
||||
if dec.Quoted(ptr) {
|
||||
return true
|
||||
}
|
||||
if dec.Literal(ptr) {
|
||||
return true
|
||||
}
|
||||
// We cannot do dec.Atom(ptr) here because sometimes mailbox names are unquoted,
|
||||
// and they can contain special characters like `]`.
|
||||
return dec.Expect(dec.Func(ptr, isAStringChar), "ASTRING-CHAR")
|
||||
}
|
||||
|
||||
func (dec *Decoder) String(ptr *string) bool {
|
||||
return dec.Quoted(ptr) || dec.Literal(ptr)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectString(ptr *string) bool {
|
||||
return dec.Expect(dec.String(ptr), "string")
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNString(ptr *string) bool {
|
||||
var s string
|
||||
if dec.Atom(&s) {
|
||||
if !dec.Expect(s == "NIL", "nstring") {
|
||||
return false
|
||||
}
|
||||
*ptr = ""
|
||||
return true
|
||||
}
|
||||
return dec.ExpectString(ptr)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNStringReader() (lit *LiteralReader, nonSync, ok bool) {
|
||||
var s string
|
||||
if dec.Atom(&s) {
|
||||
if !dec.Expect(s == "NIL", "nstring") {
|
||||
return nil, false, false
|
||||
}
|
||||
return nil, true, true
|
||||
}
|
||||
// TODO: read quoted string as a string instead of buffering
|
||||
if dec.Quoted(&s) {
|
||||
return newLiteralReaderFromString(s), true, true
|
||||
}
|
||||
if lit, nonSync, ok = dec.LiteralReader(); ok {
|
||||
return lit, nonSync, true
|
||||
} else {
|
||||
return nil, false, dec.Expect(false, "nstring")
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) List(f func() error) (isList bool, err error) {
|
||||
if !dec.Special('(') {
|
||||
return false, nil
|
||||
}
|
||||
if dec.Special(')') {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
dec.listDepth++
|
||||
defer func() {
|
||||
dec.listDepth--
|
||||
}()
|
||||
|
||||
if dec.listDepth >= maxListDepth {
|
||||
return false, fmt.Errorf("imapwire: exceeded max depth")
|
||||
}
|
||||
|
||||
for {
|
||||
if err := f(); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if dec.Special(')') {
|
||||
return true, nil
|
||||
} else if !dec.ExpectSP() {
|
||||
return true, dec.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectList(f func() error) error {
|
||||
isList, err := dec.List(f)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !dec.Expect(isList, "(") {
|
||||
return dec.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNList(f func() error) error {
|
||||
var s string
|
||||
if dec.Atom(&s) {
|
||||
if !dec.Expect(s == "NIL", "NIL") {
|
||||
return dec.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return dec.ExpectList(f)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectMailbox(ptr *string) bool {
|
||||
var name string
|
||||
if !dec.ExpectAString(&name) {
|
||||
return false
|
||||
}
|
||||
if strings.EqualFold(name, "INBOX") {
|
||||
*ptr = "INBOX"
|
||||
return true
|
||||
}
|
||||
name, err := utf7.Decode(name)
|
||||
if err == nil {
|
||||
*ptr = name
|
||||
}
|
||||
return dec.returnErr(err)
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectUID(ptr *imap.UID) bool {
|
||||
var num uint32
|
||||
if !dec.ExpectNumber(&num) {
|
||||
return false
|
||||
}
|
||||
*ptr = imap.UID(num)
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectNumSet(kind NumKind, ptr *imap.NumSet) bool {
|
||||
if dec.Special('$') {
|
||||
*ptr = imap.SearchRes()
|
||||
return true
|
||||
}
|
||||
|
||||
var s string
|
||||
if !dec.Expect(dec.Func(&s, isNumSetChar), "sequence-set") {
|
||||
return false
|
||||
}
|
||||
numSet, err := imapnum.ParseSet(s)
|
||||
if err != nil {
|
||||
return dec.returnErr(err)
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case NumKindSeq:
|
||||
*ptr = seqSetFromNumSet(numSet)
|
||||
case NumKindUID:
|
||||
*ptr = uidSetFromNumSet(numSet)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectUIDSet(ptr *imap.UIDSet) bool {
|
||||
var numSet imap.NumSet
|
||||
ok := dec.ExpectNumSet(NumKindUID, &numSet)
|
||||
if ok {
|
||||
*ptr = numSet.(imap.UIDSet)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func isNumSetChar(ch byte) bool {
|
||||
return ch == '*' || IsAtomChar(ch)
|
||||
}
|
||||
|
||||
func (dec *Decoder) Literal(ptr *string) bool {
|
||||
lit, nonSync, ok := dec.LiteralReader()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if dec.CheckBufferedLiteralFunc != nil {
|
||||
if err := dec.CheckBufferedLiteralFunc(lit.Size(), nonSync); err != nil {
|
||||
lit.cancel()
|
||||
return false
|
||||
}
|
||||
}
|
||||
var sb strings.Builder
|
||||
_, err := io.Copy(&sb, lit)
|
||||
if err == nil {
|
||||
*ptr = sb.String()
|
||||
}
|
||||
return dec.returnErr(err)
|
||||
}
|
||||
|
||||
func (dec *Decoder) LiteralReader() (lit *LiteralReader, nonSync, ok bool) {
|
||||
if !dec.Special('{') {
|
||||
return nil, false, false
|
||||
}
|
||||
var size int64
|
||||
if !dec.ExpectNumber64(&size) {
|
||||
return nil, false, false
|
||||
}
|
||||
if dec.side == ConnSideServer {
|
||||
nonSync = dec.acceptByte('+')
|
||||
}
|
||||
if !dec.ExpectSpecial('}') || !dec.ExpectCRLF() {
|
||||
return nil, false, false
|
||||
}
|
||||
dec.literal = true
|
||||
lit = &LiteralReader{
|
||||
dec: dec,
|
||||
size: size,
|
||||
r: io.LimitReader(dec.r, size),
|
||||
}
|
||||
return lit, nonSync, true
|
||||
}
|
||||
|
||||
func (dec *Decoder) ExpectLiteralReader() (lit *LiteralReader, nonSync bool, err error) {
|
||||
lit, nonSync, ok := dec.LiteralReader()
|
||||
if !dec.Expect(ok, "literal") {
|
||||
return nil, false, dec.Err()
|
||||
}
|
||||
return lit, nonSync, nil
|
||||
}
|
||||
|
||||
type LiteralReader struct {
|
||||
dec *Decoder
|
||||
size int64
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func newLiteralReaderFromString(s string) *LiteralReader {
|
||||
return &LiteralReader{
|
||||
size: int64(len(s)),
|
||||
r: strings.NewReader(s),
|
||||
}
|
||||
}
|
||||
|
||||
func (lit *LiteralReader) Size() int64 {
|
||||
return lit.size
|
||||
}
|
||||
|
||||
func (lit *LiteralReader) Read(b []byte) (int, error) {
|
||||
n, err := lit.r.Read(b)
|
||||
if err == io.EOF {
|
||||
lit.cancel()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (lit *LiteralReader) cancel() {
|
||||
if lit.dec == nil {
|
||||
return
|
||||
}
|
||||
lit.dec.literal = false
|
||||
lit.dec = nil
|
||||
}
|
||||
341
vendor/github.com/emersion/go-imap/v2/internal/imapwire/encoder.go
generated
vendored
Normal file
341
vendor/github.com/emersion/go-imap/v2/internal/imapwire/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
package imapwire
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/internal/utf7"
|
||||
)
|
||||
|
||||
// An Encoder writes IMAP data.
|
||||
//
|
||||
// Most methods don't return an error, instead they defer error handling until
|
||||
// CRLF is called. These methods return the Encoder so that calls can be
|
||||
// chained.
|
||||
type Encoder struct {
|
||||
// QuotedUTF8 allows raw UTF-8 in quoted strings. This requires IMAP4rev2
|
||||
// to be available, or UTF8=ACCEPT to be enabled.
|
||||
QuotedUTF8 bool
|
||||
// LiteralMinus enables non-synchronizing literals for short payloads.
|
||||
// This requires IMAP4rev2 or LITERAL-. This is only meaningful for
|
||||
// clients.
|
||||
LiteralMinus bool
|
||||
// LiteralPlus enables non-synchronizing literals for all payloads. This
|
||||
// requires LITERAL+. This is only meaningful for clients.
|
||||
LiteralPlus bool
|
||||
// NewContinuationRequest creates a new continuation request. This is only
|
||||
// meaningful for clients.
|
||||
NewContinuationRequest func() *ContinuationRequest
|
||||
|
||||
w *bufio.Writer
|
||||
side ConnSide
|
||||
err error
|
||||
literal bool
|
||||
}
|
||||
|
||||
// NewEncoder creates a new encoder.
|
||||
func NewEncoder(w *bufio.Writer, side ConnSide) *Encoder {
|
||||
return &Encoder{w: w, side: side}
|
||||
}
|
||||
|
||||
func (enc *Encoder) setErr(err error) {
|
||||
if enc.err == nil {
|
||||
enc.err = err
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) writeString(s string) *Encoder {
|
||||
if enc.err != nil {
|
||||
return enc
|
||||
}
|
||||
if enc.literal {
|
||||
enc.err = fmt.Errorf("imapwire: cannot encode while a literal is open")
|
||||
return enc
|
||||
}
|
||||
if _, err := enc.w.WriteString(s); err != nil {
|
||||
enc.err = err
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// CRLF writes a "\r\n" sequence and flushes the buffered writer.
|
||||
func (enc *Encoder) CRLF() error {
|
||||
enc.writeString("\r\n")
|
||||
if enc.err != nil {
|
||||
return enc.err
|
||||
}
|
||||
return enc.w.Flush()
|
||||
}
|
||||
|
||||
func (enc *Encoder) Atom(s string) *Encoder {
|
||||
return enc.writeString(s)
|
||||
}
|
||||
|
||||
func (enc *Encoder) SP() *Encoder {
|
||||
return enc.writeString(" ")
|
||||
}
|
||||
|
||||
func (enc *Encoder) Special(ch byte) *Encoder {
|
||||
return enc.writeString(string(ch))
|
||||
}
|
||||
|
||||
func (enc *Encoder) Quoted(s string) *Encoder {
|
||||
var sb strings.Builder
|
||||
sb.Grow(2 + len(s))
|
||||
sb.WriteByte('"')
|
||||
for i := 0; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
if ch == '"' || ch == '\\' {
|
||||
sb.WriteByte('\\')
|
||||
}
|
||||
sb.WriteByte(ch)
|
||||
}
|
||||
sb.WriteByte('"')
|
||||
return enc.writeString(sb.String())
|
||||
}
|
||||
|
||||
func (enc *Encoder) String(s string) *Encoder {
|
||||
if !enc.validQuoted(s) {
|
||||
enc.stringLiteral(s)
|
||||
return enc
|
||||
}
|
||||
return enc.Quoted(s)
|
||||
}
|
||||
|
||||
func (enc *Encoder) validQuoted(s string) bool {
|
||||
if len(s) > 4096 {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
|
||||
// NUL, CR and LF are never valid
|
||||
switch ch {
|
||||
case 0, '\r', '\n':
|
||||
return false
|
||||
}
|
||||
|
||||
if !enc.QuotedUTF8 && ch > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (enc *Encoder) stringLiteral(s string) {
|
||||
var sync *ContinuationRequest
|
||||
if enc.side == ConnSideClient && (!enc.LiteralMinus || len(s) > 4096) && !enc.LiteralPlus {
|
||||
if enc.NewContinuationRequest != nil {
|
||||
sync = enc.NewContinuationRequest()
|
||||
}
|
||||
if sync == nil {
|
||||
enc.setErr(fmt.Errorf("imapwire: cannot send synchronizing literal"))
|
||||
return
|
||||
}
|
||||
}
|
||||
wc := enc.Literal(int64(len(s)), sync)
|
||||
_, writeErr := io.WriteString(wc, s)
|
||||
closeErr := wc.Close()
|
||||
if writeErr != nil {
|
||||
enc.setErr(writeErr)
|
||||
} else if closeErr != nil {
|
||||
enc.setErr(closeErr)
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) Mailbox(name string) *Encoder {
|
||||
if strings.EqualFold(name, "INBOX") {
|
||||
return enc.Atom("INBOX")
|
||||
} else {
|
||||
if enc.QuotedUTF8 {
|
||||
name = utf7.Escape(name)
|
||||
} else {
|
||||
name = utf7.Encode(name)
|
||||
}
|
||||
return enc.String(name)
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) NumSet(numSet imap.NumSet) *Encoder {
|
||||
s := numSet.String()
|
||||
if s == "" {
|
||||
enc.setErr(fmt.Errorf("imapwire: cannot encode empty sequence set"))
|
||||
return enc
|
||||
}
|
||||
return enc.writeString(s)
|
||||
}
|
||||
|
||||
func (enc *Encoder) Flag(flag imap.Flag) *Encoder {
|
||||
if flag != "\\*" && !isValidFlag(string(flag)) {
|
||||
enc.setErr(fmt.Errorf("imapwire: invalid flag %q", flag))
|
||||
return enc
|
||||
}
|
||||
return enc.writeString(string(flag))
|
||||
}
|
||||
|
||||
func (enc *Encoder) MailboxAttr(attr imap.MailboxAttr) *Encoder {
|
||||
if !strings.HasPrefix(string(attr), "\\") || !isValidFlag(string(attr)) {
|
||||
enc.setErr(fmt.Errorf("imapwire: invalid mailbox attribute %q", attr))
|
||||
return enc
|
||||
}
|
||||
return enc.writeString(string(attr))
|
||||
}
|
||||
|
||||
// isValidFlag checks whether the provided string satisfies
|
||||
// flag-keyword / flag-extension.
|
||||
func isValidFlag(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
if ch == '\\' {
|
||||
if i != 0 {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !IsAtomChar(ch) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(s) > 0
|
||||
}
|
||||
|
||||
func (enc *Encoder) Number(v uint32) *Encoder {
|
||||
return enc.writeString(strconv.FormatUint(uint64(v), 10))
|
||||
}
|
||||
|
||||
func (enc *Encoder) Number64(v int64) *Encoder {
|
||||
// TODO: disallow negative values
|
||||
return enc.writeString(strconv.FormatInt(v, 10))
|
||||
}
|
||||
|
||||
func (enc *Encoder) ModSeq(v uint64) *Encoder {
|
||||
// TODO: disallow zero values
|
||||
return enc.writeString(strconv.FormatUint(v, 10))
|
||||
}
|
||||
|
||||
// List writes a parenthesized list.
|
||||
func (enc *Encoder) List(n int, f func(i int)) *Encoder {
|
||||
enc.Special('(')
|
||||
for i := 0; i < n; i++ {
|
||||
if i > 0 {
|
||||
enc.SP()
|
||||
}
|
||||
f(i)
|
||||
}
|
||||
enc.Special(')')
|
||||
return enc
|
||||
}
|
||||
|
||||
func (enc *Encoder) BeginList() *ListEncoder {
|
||||
enc.Special('(')
|
||||
return &ListEncoder{enc: enc}
|
||||
}
|
||||
|
||||
func (enc *Encoder) NIL() *Encoder {
|
||||
return enc.Atom("NIL")
|
||||
}
|
||||
|
||||
func (enc *Encoder) Text(s string) *Encoder {
|
||||
return enc.writeString(s)
|
||||
}
|
||||
|
||||
func (enc *Encoder) UID(uid imap.UID) *Encoder {
|
||||
return enc.Number(uint32(uid))
|
||||
}
|
||||
|
||||
// Literal writes a literal.
|
||||
//
|
||||
// The caller must write exactly size bytes to the returned writer.
|
||||
//
|
||||
// If sync is non-nil, the literal is synchronizing: the encoder will wait for
|
||||
// nil to be sent to the channel before writing the literal data. If an error
|
||||
// is sent to the channel, the literal will be cancelled.
|
||||
func (enc *Encoder) Literal(size int64, sync *ContinuationRequest) io.WriteCloser {
|
||||
if sync != nil && enc.side == ConnSideServer {
|
||||
panic("imapwire: sync must be nil on a server-side Encoder.Literal")
|
||||
}
|
||||
|
||||
// TODO: literal8
|
||||
enc.writeString("{")
|
||||
enc.Number64(size)
|
||||
if sync == nil && enc.side == ConnSideClient {
|
||||
enc.writeString("+")
|
||||
}
|
||||
enc.writeString("}")
|
||||
|
||||
if sync == nil {
|
||||
enc.writeString("\r\n")
|
||||
} else {
|
||||
if err := enc.CRLF(); err != nil {
|
||||
return errorWriter{err}
|
||||
}
|
||||
if _, err := sync.Wait(); err != nil {
|
||||
enc.setErr(err)
|
||||
return errorWriter{err}
|
||||
}
|
||||
}
|
||||
|
||||
enc.literal = true
|
||||
return &literalWriter{
|
||||
enc: enc,
|
||||
n: size,
|
||||
}
|
||||
}
|
||||
|
||||
type errorWriter struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (ew errorWriter) Write(b []byte) (int, error) {
|
||||
return 0, ew.err
|
||||
}
|
||||
|
||||
func (ew errorWriter) Close() error {
|
||||
return ew.err
|
||||
}
|
||||
|
||||
type literalWriter struct {
|
||||
enc *Encoder
|
||||
n int64
|
||||
}
|
||||
|
||||
func (lw *literalWriter) Write(b []byte) (int, error) {
|
||||
if lw.n-int64(len(b)) < 0 {
|
||||
return 0, fmt.Errorf("wrote too many bytes in literal")
|
||||
}
|
||||
n, err := lw.enc.w.Write(b)
|
||||
lw.n -= int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (lw *literalWriter) Close() error {
|
||||
lw.enc.literal = false
|
||||
if lw.n != 0 {
|
||||
return fmt.Errorf("wrote too few bytes in literal (%v remaining)", lw.n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListEncoder struct {
|
||||
enc *Encoder
|
||||
n int
|
||||
}
|
||||
|
||||
func (le *ListEncoder) Item() *Encoder {
|
||||
if le.n > 0 {
|
||||
le.enc.SP()
|
||||
}
|
||||
le.n++
|
||||
return le.enc
|
||||
}
|
||||
|
||||
func (le *ListEncoder) End() {
|
||||
le.enc.Special(')')
|
||||
le.enc = nil
|
||||
}
|
||||
47
vendor/github.com/emersion/go-imap/v2/internal/imapwire/imapwire.go
generated
vendored
Normal file
47
vendor/github.com/emersion/go-imap/v2/internal/imapwire/imapwire.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Package imapwire implements the IMAP wire protocol.
|
||||
//
|
||||
// The IMAP wire protocol is defined in RFC 9051 section 4.
|
||||
package imapwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ConnSide describes the side of a connection: client or server.
|
||||
type ConnSide int
|
||||
|
||||
const (
|
||||
ConnSideClient ConnSide = 1 + iota
|
||||
ConnSideServer
|
||||
)
|
||||
|
||||
// ContinuationRequest is a continuation request.
|
||||
//
|
||||
// The sender must call either Done or Cancel. The receiver must call Wait.
|
||||
type ContinuationRequest struct {
|
||||
done chan struct{}
|
||||
err error
|
||||
text string
|
||||
}
|
||||
|
||||
func NewContinuationRequest() *ContinuationRequest {
|
||||
return &ContinuationRequest{done: make(chan struct{})}
|
||||
}
|
||||
|
||||
func (cont *ContinuationRequest) Cancel(err error) {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("imapwire: continuation request cancelled")
|
||||
}
|
||||
cont.err = err
|
||||
close(cont.done)
|
||||
}
|
||||
|
||||
func (cont *ContinuationRequest) Done(text string) {
|
||||
cont.text = text
|
||||
close(cont.done)
|
||||
}
|
||||
|
||||
func (cont *ContinuationRequest) Wait() (string, error) {
|
||||
<-cont.done
|
||||
return cont.text, cont.err
|
||||
}
|
||||
39
vendor/github.com/emersion/go-imap/v2/internal/imapwire/num.go
generated
vendored
Normal file
39
vendor/github.com/emersion/go-imap/v2/internal/imapwire/num.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package imapwire
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/internal/imapnum"
|
||||
)
|
||||
|
||||
type NumKind int
|
||||
|
||||
const (
|
||||
NumKindSeq NumKind = iota + 1
|
||||
NumKindUID
|
||||
)
|
||||
|
||||
func seqSetFromNumSet(s imapnum.Set) imap.SeqSet {
|
||||
return *(*imap.SeqSet)(unsafe.Pointer(&s))
|
||||
}
|
||||
|
||||
func uidSetFromNumSet(s imapnum.Set) imap.UIDSet {
|
||||
return *(*imap.UIDSet)(unsafe.Pointer(&s))
|
||||
}
|
||||
|
||||
func NumSetKind(numSet imap.NumSet) NumKind {
|
||||
switch numSet.(type) {
|
||||
case imap.SeqSet:
|
||||
return NumKindSeq
|
||||
case imap.UIDSet:
|
||||
return NumKindUID
|
||||
default:
|
||||
panic("imap: invalid NumSet type")
|
||||
}
|
||||
}
|
||||
|
||||
func ParseSeqSet(s string) (imap.SeqSet, error) {
|
||||
numSet, err := imapnum.ParseSet(s)
|
||||
return seqSetFromNumSet(numSet), err
|
||||
}
|
||||
188
vendor/github.com/emersion/go-imap/v2/internal/internal.go
generated
vendored
Normal file
188
vendor/github.com/emersion/go-imap/v2/internal/internal.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/internal/imapwire"
|
||||
)
|
||||
|
||||
const (
|
||||
DateTimeLayout = "_2-Jan-2006 15:04:05 -0700"
|
||||
DateLayout = "2-Jan-2006"
|
||||
)
|
||||
|
||||
const FlagRecent imap.Flag = "\\Recent" // removed in IMAP4rev2
|
||||
|
||||
func DecodeDateTime(dec *imapwire.Decoder) (time.Time, error) {
|
||||
var s string
|
||||
if !dec.Quoted(&s) {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
t, err := time.Parse(DateTimeLayout, s)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("in date-time: %v", err) // TODO: use imapwire.DecodeExpectError?
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
func ExpectDateTime(dec *imapwire.Decoder) (time.Time, error) {
|
||||
t, err := DecodeDateTime(dec)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
if !dec.Expect(!t.IsZero(), "date-time") {
|
||||
return t, dec.Err()
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func ExpectDate(dec *imapwire.Decoder) (time.Time, error) {
|
||||
var s string
|
||||
if !dec.ExpectAString(&s) {
|
||||
return time.Time{}, dec.Err()
|
||||
}
|
||||
t, err := time.Parse(DateLayout, s)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("in date: %v", err) // use imapwire.DecodeExpectError?
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func ExpectFlagList(dec *imapwire.Decoder) ([]imap.Flag, error) {
|
||||
var flags []imap.Flag
|
||||
err := dec.ExpectList(func() error {
|
||||
// Some servers start the list with a space, so we need to skip it
|
||||
// https://github.com/emersion/go-imap/pull/633
|
||||
dec.SP()
|
||||
|
||||
flag, err := ExpectFlag(dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flags = append(flags, flag)
|
||||
return nil
|
||||
})
|
||||
return flags, err
|
||||
}
|
||||
|
||||
func ExpectCap(dec *imapwire.Decoder) (imap.Cap, error) {
|
||||
var name string
|
||||
if !dec.ExpectAtom(&name) {
|
||||
return "", dec.Err()
|
||||
}
|
||||
return canonicalCap(name), nil
|
||||
}
|
||||
|
||||
func ExpectFlag(dec *imapwire.Decoder) (imap.Flag, error) {
|
||||
isSystem := dec.Special('\\')
|
||||
if isSystem && dec.Special('*') {
|
||||
return imap.FlagWildcard, nil // flag-perm
|
||||
}
|
||||
var name string
|
||||
if !dec.ExpectAtom(&name) {
|
||||
return "", fmt.Errorf("in flag: %w", dec.Err())
|
||||
}
|
||||
if isSystem {
|
||||
name = "\\" + name
|
||||
}
|
||||
return canonicalFlag(name), nil
|
||||
}
|
||||
|
||||
func ExpectMailboxAttrList(dec *imapwire.Decoder) ([]imap.MailboxAttr, error) {
|
||||
var attrs []imap.MailboxAttr
|
||||
err := dec.ExpectList(func() error {
|
||||
attr, err := ExpectMailboxAttr(dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attrs = append(attrs, attr)
|
||||
return nil
|
||||
})
|
||||
return attrs, err
|
||||
}
|
||||
|
||||
func ExpectMailboxAttr(dec *imapwire.Decoder) (imap.MailboxAttr, error) {
|
||||
flag, err := ExpectFlag(dec)
|
||||
return canonicalMailboxAttr(string(flag)), err
|
||||
}
|
||||
|
||||
var (
|
||||
canonOnce sync.Once
|
||||
canonFlag map[string]imap.Flag
|
||||
canonMailboxAttr map[string]imap.MailboxAttr
|
||||
)
|
||||
|
||||
func canonInit() {
|
||||
flags := []imap.Flag{
|
||||
imap.FlagSeen,
|
||||
imap.FlagAnswered,
|
||||
imap.FlagFlagged,
|
||||
imap.FlagDeleted,
|
||||
imap.FlagDraft,
|
||||
imap.FlagForwarded,
|
||||
imap.FlagMDNSent,
|
||||
imap.FlagJunk,
|
||||
imap.FlagNotJunk,
|
||||
imap.FlagPhishing,
|
||||
imap.FlagImportant,
|
||||
}
|
||||
mailboxAttrs := []imap.MailboxAttr{
|
||||
imap.MailboxAttrNonExistent,
|
||||
imap.MailboxAttrNoInferiors,
|
||||
imap.MailboxAttrNoSelect,
|
||||
imap.MailboxAttrHasChildren,
|
||||
imap.MailboxAttrHasNoChildren,
|
||||
imap.MailboxAttrMarked,
|
||||
imap.MailboxAttrUnmarked,
|
||||
imap.MailboxAttrSubscribed,
|
||||
imap.MailboxAttrRemote,
|
||||
imap.MailboxAttrAll,
|
||||
imap.MailboxAttrArchive,
|
||||
imap.MailboxAttrDrafts,
|
||||
imap.MailboxAttrFlagged,
|
||||
imap.MailboxAttrJunk,
|
||||
imap.MailboxAttrSent,
|
||||
imap.MailboxAttrTrash,
|
||||
imap.MailboxAttrImportant,
|
||||
}
|
||||
|
||||
canonFlag = make(map[string]imap.Flag)
|
||||
for _, flag := range flags {
|
||||
canonFlag[strings.ToLower(string(flag))] = flag
|
||||
}
|
||||
|
||||
canonMailboxAttr = make(map[string]imap.MailboxAttr)
|
||||
for _, attr := range mailboxAttrs {
|
||||
canonMailboxAttr[strings.ToLower(string(attr))] = attr
|
||||
}
|
||||
}
|
||||
|
||||
func canonicalFlag(s string) imap.Flag {
|
||||
canonOnce.Do(canonInit)
|
||||
if flag, ok := canonFlag[strings.ToLower(s)]; ok {
|
||||
return flag
|
||||
}
|
||||
return imap.Flag(s)
|
||||
}
|
||||
|
||||
func canonicalMailboxAttr(s string) imap.MailboxAttr {
|
||||
canonOnce.Do(canonInit)
|
||||
if attr, ok := canonMailboxAttr[strings.ToLower(s)]; ok {
|
||||
return attr
|
||||
}
|
||||
return imap.MailboxAttr(s)
|
||||
}
|
||||
|
||||
func canonicalCap(s string) imap.Cap {
|
||||
// Only two caps are not fully uppercase
|
||||
for _, cap := range []imap.Cap{imap.CapIMAP4rev1, imap.CapIMAP4rev2} {
|
||||
if strings.EqualFold(s, string(cap)) {
|
||||
return cap
|
||||
}
|
||||
}
|
||||
return imap.Cap(strings.ToUpper(s))
|
||||
}
|
||||
23
vendor/github.com/emersion/go-imap/v2/internal/sasl.go
generated
vendored
Normal file
23
vendor/github.com/emersion/go-imap/v2/internal/sasl.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func EncodeSASL(b []byte) string {
|
||||
if len(b) == 0 {
|
||||
return "="
|
||||
} else {
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
}
|
||||
|
||||
func DecodeSASL(s string) ([]byte, error) {
|
||||
if s == "=" {
|
||||
// go-sasl treats nil as no challenge/response, so return a non-nil
|
||||
// empty byte slice
|
||||
return []byte{}, nil
|
||||
} else {
|
||||
return base64.StdEncoding.DecodeString(s)
|
||||
}
|
||||
}
|
||||
118
vendor/github.com/emersion/go-imap/v2/internal/utf7/decoder.go
generated
vendored
Normal file
118
vendor/github.com/emersion/go-imap/v2/internal/utf7/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ErrInvalidUTF7 means that a decoder encountered invalid UTF-7.
|
||||
var ErrInvalidUTF7 = errors.New("utf7: invalid UTF-7")
|
||||
|
||||
// Decode decodes a string encoded with modified UTF-7.
|
||||
//
|
||||
// Note, raw UTF-8 is accepted.
|
||||
func Decode(src string) (string, error) {
|
||||
if !utf8.ValidString(src) {
|
||||
return "", errors.New("invalid UTF-8")
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(src))
|
||||
|
||||
ascii := true
|
||||
for i := 0; i < len(src); i++ {
|
||||
ch := src[i]
|
||||
|
||||
if ch < min || (ch > max && ch < utf8.RuneSelf) {
|
||||
// Illegal code point in ASCII mode. Note, UTF-8 codepoints are
|
||||
// always allowed.
|
||||
return "", ErrInvalidUTF7
|
||||
}
|
||||
|
||||
if ch != '&' {
|
||||
sb.WriteByte(ch)
|
||||
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
|
||||
return "", ErrInvalidUTF7
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(src) { // Implicit shift ("&...")
|
||||
return "", ErrInvalidUTF7
|
||||
}
|
||||
|
||||
if i == start { // Escape sequence "&-"
|
||||
sb.WriteByte('&')
|
||||
ascii = true
|
||||
} else { // Control or non-ASCII code points in base64
|
||||
if !ascii { // Null shift ("&...-&...-")
|
||||
return "", ErrInvalidUTF7
|
||||
}
|
||||
|
||||
b := decode([]byte(src[start:i]))
|
||||
if len(b) == 0 { // Bad encoding
|
||||
return "", ErrInvalidUTF7
|
||||
}
|
||||
sb.Write(b)
|
||||
|
||||
ascii = false
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
// 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 == utf8.RuneError {
|
||||
return nil
|
||||
}
|
||||
} else if min <= r && r <= max {
|
||||
return nil
|
||||
}
|
||||
j += utf8.EncodeRune(s[j:], r)
|
||||
}
|
||||
return s[:j]
|
||||
}
|
||||
88
vendor/github.com/emersion/go-imap/v2/internal/utf7/encoder.go
generated
vendored
Normal file
88
vendor/github.com/emersion/go-imap/v2/internal/utf7/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Encode encodes a string with modified UTF-7.
|
||||
func Encode(src string) string {
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(src))
|
||||
|
||||
for i := 0; i < len(src); {
|
||||
ch := src[i]
|
||||
|
||||
if min <= ch && ch <= max {
|
||||
sb.WriteByte(ch)
|
||||
if ch == '&' {
|
||||
sb.WriteByte('-')
|
||||
}
|
||||
|
||||
i++
|
||||
} else {
|
||||
start := i
|
||||
|
||||
// Find the next printable ASCII code point
|
||||
i++
|
||||
for i < len(src) && (src[i] < min || src[i] > max) {
|
||||
i++
|
||||
}
|
||||
|
||||
sb.Write(encode([]byte(src[start:i])))
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// 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 != utf8.RuneError {
|
||||
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
|
||||
}
|
||||
|
||||
// Escape passes through raw UTF-8 as-is and escapes the special UTF-7 marker
|
||||
// (the ampersand character).
|
||||
func Escape(src string) string {
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(src))
|
||||
|
||||
for _, ch := range src {
|
||||
sb.WriteRune(ch)
|
||||
if ch == '&' {
|
||||
sb.WriteByte('-')
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
13
vendor/github.com/emersion/go-imap/v2/internal/utf7/utf7.go
generated
vendored
Normal file
13
vendor/github.com/emersion/go-imap/v2/internal/utf7/utf7.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package utf7 implements modified UTF-7 encoding defined in RFC 3501 section 5.1.3
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
const (
|
||||
min = 0x20 // Minimum self-representing UTF-7 value
|
||||
max = 0x7E // Maximum self-representing UTF-7 value
|
||||
)
|
||||
|
||||
var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
|
||||
Reference in New Issue
Block a user