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 }