1// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package bsoncore
8
9import (
10	"bytes"
11	"errors"
12	"fmt"
13	"io"
14	"strconv"
15
16	"github.com/go-stack/stack"
17	"go.mongodb.org/mongo-driver/bson/bsontype"
18)
19
20// ValidationError is an error type returned when attempting to validate a document or array.
21type ValidationError string
22
23func (ve ValidationError) Error() string { return string(ve) }
24
25// NewDocumentLengthError creates and returns an error for when the length of a document exceeds the
26// bytes available.
27func NewDocumentLengthError(length, rem int) error {
28	return lengthError("document", length, rem)
29}
30
31func lengthError(bufferType string, length, rem int) error {
32	return ValidationError(fmt.Sprintf("%v length exceeds available bytes. length=%d remainingBytes=%d",
33		bufferType, length, rem))
34}
35
36// InsufficientBytesError indicates that there were not enough bytes to read the next component.
37type InsufficientBytesError struct {
38	Source    []byte
39	Remaining []byte
40	Stack     stack.CallStack
41}
42
43// NewInsufficientBytesError creates a new InsufficientBytesError with the given Document, remaining
44// bytes, and the current stack.
45func NewInsufficientBytesError(src, rem []byte) InsufficientBytesError {
46	return InsufficientBytesError{Source: src, Remaining: rem, Stack: stack.Trace().TrimRuntime()}
47}
48
49// Error implements the error interface.
50func (ibe InsufficientBytesError) Error() string {
51	return "too few bytes to read next component"
52}
53
54// ErrorStack returns a string representing the stack at the point where the error occurred.
55func (ibe InsufficientBytesError) ErrorStack() string {
56	s := bytes.NewBufferString("too few bytes to read next component: [")
57
58	for i, call := range ibe.Stack {
59		if i != 0 {
60			s.WriteString(", ")
61		}
62
63		// go vet doesn't like %k even though it's part of stack's API, so we move the format
64		// string so it doesn't complain. (We also can't make it a constant, or go vet still
65		// complains.)
66		callFormat := "%k.%n %v"
67
68		s.WriteString(fmt.Sprintf(callFormat, call, call, call))
69	}
70
71	s.WriteRune(']')
72
73	return s.String()
74}
75
76// Equal checks that err2 also is an ErrTooSmall.
77func (ibe InsufficientBytesError) Equal(err2 error) bool {
78	switch err2.(type) {
79	case InsufficientBytesError:
80		return true
81	default:
82		return false
83	}
84}
85
86// InvalidDepthTraversalError is returned when attempting a recursive Lookup when one component of
87// the path is neither an embedded document nor an array.
88type InvalidDepthTraversalError struct {
89	Key  string
90	Type bsontype.Type
91}
92
93func (idte InvalidDepthTraversalError) Error() string {
94	return fmt.Sprintf(
95		"attempt to traverse into %s, but it's type is %s, not %s nor %s",
96		idte.Key, idte.Type, bsontype.EmbeddedDocument, bsontype.Array,
97	)
98}
99
100// ErrMissingNull is returned when a document or array's last byte is not null.
101const ErrMissingNull ValidationError = "document or array end is missing null byte"
102
103// ErrInvalidLength indicates that a length in a binary representation of a BSON document or array
104// is invalid.
105const ErrInvalidLength ValidationError = "document or array length is invalid"
106
107// ErrNilReader indicates that an operation was attempted on a nil io.Reader.
108var ErrNilReader = errors.New("nil reader")
109
110// ErrEmptyKey indicates that no key was provided to a Lookup method.
111var ErrEmptyKey = errors.New("empty key provided")
112
113// ErrElementNotFound indicates that an Element matching a certain condition does not exist.
114var ErrElementNotFound = errors.New("element not found")
115
116// ErrOutOfBounds indicates that an index provided to access something was invalid.
117var ErrOutOfBounds = errors.New("out of bounds")
118
119// Document is a raw bytes representation of a BSON document.
120type Document []byte
121
122// NewDocumentFromReader reads a document from r. This function will only validate the length is
123// correct and that the document ends with a null byte.
124func NewDocumentFromReader(r io.Reader) (Document, error) {
125	return newBufferFromReader(r)
126}
127
128func newBufferFromReader(r io.Reader) ([]byte, error) {
129	if r == nil {
130		return nil, ErrNilReader
131	}
132
133	var lengthBytes [4]byte
134
135	// ReadFull guarantees that we will have read at least len(lengthBytes) if err == nil
136	_, err := io.ReadFull(r, lengthBytes[:])
137	if err != nil {
138		return nil, err
139	}
140
141	length, _, _ := readi32(lengthBytes[:]) // ignore ok since we always have enough bytes to read a length
142	if length < 0 {
143		return nil, ErrInvalidLength
144	}
145	buffer := make([]byte, length)
146
147	copy(buffer, lengthBytes[:])
148
149	_, err = io.ReadFull(r, buffer[4:])
150	if err != nil {
151		return nil, err
152	}
153
154	if buffer[length-1] != 0x00 {
155		return nil, ErrMissingNull
156	}
157
158	return buffer, nil
159}
160
161// Lookup searches the document, potentially recursively, for the given key. If there are multiple
162// keys provided, this method will recurse down, as long as the top and intermediate nodes are
163// either documents or arrays. If an error occurs or if the value doesn't exist, an empty Value is
164// returned.
165func (d Document) Lookup(key ...string) Value {
166	val, _ := d.LookupErr(key...)
167	return val
168}
169
170// LookupErr is the same as Lookup, except it returns an error in addition to an empty Value.
171func (d Document) LookupErr(key ...string) (Value, error) {
172	if len(key) < 1 {
173		return Value{}, ErrEmptyKey
174	}
175	length, rem, ok := ReadLength(d)
176	if !ok {
177		return Value{}, NewInsufficientBytesError(d, rem)
178	}
179
180	length -= 4
181
182	var elem Element
183	for length > 1 {
184		elem, rem, ok = ReadElement(rem)
185		length -= int32(len(elem))
186		if !ok {
187			return Value{}, NewInsufficientBytesError(d, rem)
188		}
189		// We use `KeyBytes` rather than `Key` to avoid a needless string alloc.
190		if string(elem.KeyBytes()) != key[0] {
191			continue
192		}
193		if len(key) > 1 {
194			tt := bsontype.Type(elem[0])
195			switch tt {
196			case bsontype.EmbeddedDocument:
197				val, err := elem.Value().Document().LookupErr(key[1:]...)
198				if err != nil {
199					return Value{}, err
200				}
201				return val, nil
202			case bsontype.Array:
203				val, err := elem.Value().Array().LookupErr(key[1:]...)
204				if err != nil {
205					return Value{}, err
206				}
207				return val, nil
208			default:
209				return Value{}, InvalidDepthTraversalError{Key: elem.Key(), Type: tt}
210			}
211		}
212		return elem.ValueErr()
213	}
214	return Value{}, ErrElementNotFound
215}
216
217// Index searches for and retrieves the element at the given index. This method will panic if
218// the document is invalid or if the index is out of bounds.
219func (d Document) Index(index uint) Element {
220	elem, err := d.IndexErr(index)
221	if err != nil {
222		panic(err)
223	}
224	return elem
225}
226
227// IndexErr searches for and retrieves the element at the given index.
228func (d Document) IndexErr(index uint) (Element, error) {
229	return indexErr(d, index)
230}
231
232func indexErr(b []byte, index uint) (Element, error) {
233	length, rem, ok := ReadLength(b)
234	if !ok {
235		return nil, NewInsufficientBytesError(b, rem)
236	}
237
238	length -= 4
239
240	var current uint
241	var elem Element
242	for length > 1 {
243		elem, rem, ok = ReadElement(rem)
244		length -= int32(len(elem))
245		if !ok {
246			return nil, NewInsufficientBytesError(b, rem)
247		}
248		if current != index {
249			current++
250			continue
251		}
252		return elem, nil
253	}
254	return nil, ErrOutOfBounds
255}
256
257// DebugString outputs a human readable version of Document. It will attempt to stringify the
258// valid components of the document even if the entire document is not valid.
259func (d Document) DebugString() string {
260	if len(d) < 5 {
261		return "<malformed>"
262	}
263	var buf bytes.Buffer
264	buf.WriteString("Document")
265	length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length
266	buf.WriteByte('(')
267	buf.WriteString(strconv.Itoa(int(length)))
268	length -= 4
269	buf.WriteString("){")
270	var elem Element
271	var ok bool
272	for length > 1 {
273		elem, rem, ok = ReadElement(rem)
274		length -= int32(len(elem))
275		if !ok {
276			buf.WriteString(fmt.Sprintf("<malformed (%d)>", length))
277			break
278		}
279		fmt.Fprintf(&buf, "%s ", elem.DebugString())
280	}
281	buf.WriteByte('}')
282
283	return buf.String()
284}
285
286// String outputs an ExtendedJSON version of Document. If the document is not valid, this method
287// returns an empty string.
288func (d Document) String() string {
289	if len(d) < 5 {
290		return ""
291	}
292	var buf bytes.Buffer
293	buf.WriteByte('{')
294
295	length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length
296
297	length -= 4
298
299	var elem Element
300	var ok bool
301	first := true
302	for length > 1 {
303		if !first {
304			buf.WriteByte(',')
305		}
306		elem, rem, ok = ReadElement(rem)
307		length -= int32(len(elem))
308		if !ok {
309			return ""
310		}
311		fmt.Fprintf(&buf, "%s", elem.String())
312		first = false
313	}
314	buf.WriteByte('}')
315
316	return buf.String()
317}
318
319// Elements returns this document as a slice of elements. The returned slice will contain valid
320// elements. If the document is not valid, the elements up to the invalid point will be returned
321// along with an error.
322func (d Document) Elements() ([]Element, error) {
323	length, rem, ok := ReadLength(d)
324	if !ok {
325		return nil, NewInsufficientBytesError(d, rem)
326	}
327
328	length -= 4
329
330	var elem Element
331	var elems []Element
332	for length > 1 {
333		elem, rem, ok = ReadElement(rem)
334		length -= int32(len(elem))
335		if !ok {
336			return elems, NewInsufficientBytesError(d, rem)
337		}
338		if err := elem.Validate(); err != nil {
339			return elems, err
340		}
341		elems = append(elems, elem)
342	}
343	return elems, nil
344}
345
346// Values returns this document as a slice of values. The returned slice will contain valid values.
347// If the document is not valid, the values up to the invalid point will be returned along with an
348// error.
349func (d Document) Values() ([]Value, error) {
350	return values(d)
351}
352
353func values(b []byte) ([]Value, error) {
354	length, rem, ok := ReadLength(b)
355	if !ok {
356		return nil, NewInsufficientBytesError(b, rem)
357	}
358
359	length -= 4
360
361	var elem Element
362	var vals []Value
363	for length > 1 {
364		elem, rem, ok = ReadElement(rem)
365		length -= int32(len(elem))
366		if !ok {
367			return vals, NewInsufficientBytesError(b, rem)
368		}
369		if err := elem.Value().Validate(); err != nil {
370			return vals, err
371		}
372		vals = append(vals, elem.Value())
373	}
374	return vals, nil
375}
376
377// Validate validates the document and ensures the elements contained within are valid.
378func (d Document) Validate() error {
379	length, rem, ok := ReadLength(d)
380	if !ok {
381		return NewInsufficientBytesError(d, rem)
382	}
383	if int(length) > len(d) {
384		return NewDocumentLengthError(int(length), len(d))
385	}
386	if d[length-1] != 0x00 {
387		return ErrMissingNull
388	}
389
390	length -= 4
391	var elem Element
392
393	for length > 1 {
394		elem, rem, ok = ReadElement(rem)
395		length -= int32(len(elem))
396		if !ok {
397			return NewInsufficientBytesError(d, rem)
398		}
399		err := elem.Validate()
400		if err != nil {
401			return err
402		}
403	}
404
405	if len(rem) < 1 || rem[0] != 0x00 {
406		return ErrMissingNull
407	}
408	return nil
409}
410