1// Copyright (C) MongoDB, Inc. 2014-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
7// Package bsonutil provides utilities for processing BSON data.
8package bsonutil
9
10import (
11	"encoding/base64"
12	"encoding/hex"
13	"errors"
14	"fmt"
15	"strconv"
16	"time"
17
18	"github.com/mongodb/mongo-tools-common/json"
19	"github.com/mongodb/mongo-tools-common/util"
20	"go.mongodb.org/mongo-driver/bson"
21	"go.mongodb.org/mongo-driver/bson/primitive"
22)
23
24var ErrNoSuchField = errors.New("no such field")
25
26// ConvertLegacyExtJSONDocumentToBSON iterates through the document map and converts JSON
27// values to their corresponding BSON values. It also replaces any extended JSON
28// type value (e.g. $date) with the corresponding BSON type
29func ConvertLegacyExtJSONDocumentToBSON(doc map[string]interface{}) error {
30	for key, jsonValue := range doc {
31		var bsonValue interface{}
32		var err error
33
34		switch v := jsonValue.(type) {
35		case map[string]interface{}, bson.D: // subdocument
36			bsonValue, err = ParseSpecialKeys(v)
37		default:
38			bsonValue, err = ConvertLegacyExtJSONValueToBSON(v)
39		}
40		if err != nil {
41			return err
42		}
43
44		doc[key] = bsonValue
45	}
46	return nil
47}
48
49// GetExtendedBsonD iterates through the document and returns a bson.D that adds type
50// information for each key in document.
51func GetExtendedBsonD(doc bson.D) (bson.D, error) {
52	var err error
53	var bsonDoc bson.D
54	for _, docElem := range doc {
55		var bsonValue interface{}
56		switch v := docElem.Value.(type) {
57		case map[string]interface{}, bson.D: // subdocument
58			bsonValue, err = ParseSpecialKeys(v)
59		default:
60			bsonValue, err = ConvertLegacyExtJSONValueToBSON(v)
61		}
62		if err != nil {
63			return nil, err
64		}
65		bsonDoc = append(bsonDoc, bson.E{
66			Key:   docElem.Key,
67			Value: bsonValue,
68		})
69	}
70	return bsonDoc, nil
71}
72
73// FindValueByKey returns the value of keyName in document. If keyName is not found
74// in the top-level of the document, ErrNoSuchField is returned as the error.
75func FindValueByKey(keyName string, document *bson.D) (interface{}, error) {
76	for _, key := range *document {
77		if key.Key == keyName {
78			return key.Value, nil
79		}
80	}
81	return nil, ErrNoSuchField
82}
83
84// FindIntByKey returns the value of keyName in the document as an int for
85// either int32 or int64 underlying type.
86func FindIntByKey(keyName string, document *bson.D) (int, error) {
87	raw, err := FindValueByKey(keyName, document)
88	if err != nil {
89		return 0, err
90	}
91	switch x := raw.(type) {
92	case int32:
93		return int(x), nil
94	case int64:
95		return int(x), nil
96	case int:
97		return x, nil
98	default:
99		return 0, fmt.Errorf("field '%s' is not an integer type", keyName)
100	}
101}
102
103// FindSubdocumentByKey returns the value of keyName in document as a document.
104// Returns an error if keyName is not found in the top-level of the document,
105// or if it is found but its value is not a document.
106func FindSubdocumentByKey(keyName string, document *bson.D) (bson.D, error) {
107	value, err := FindValueByKey(keyName, document)
108	if err != nil {
109		return bson.D{}, err
110	}
111	doc, ok := value.(bson.D)
112	if !ok {
113		return bson.D{}, fmt.Errorf("field '%s' is not a document", keyName)
114	}
115	return doc, nil
116}
117
118// ParseSpecialKeys takes a JSON document and inspects it for any extended JSON
119// type (e.g $numberLong) and replaces any such values with the corresponding
120// BSON type. (uses legacy extJSON parser)
121func ParseSpecialKeys(special interface{}) (interface{}, error) {
122	// first ensure we are using a correct document type
123	var doc map[string]interface{}
124	switch v := special.(type) {
125	case bson.D:
126		doc = v.Map()
127	case map[string]interface{}:
128		doc = v
129	default:
130		return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special)
131	}
132	// check document to see if it is special
133	switch len(doc) {
134	case 1: // document has a single field
135		if jsonValue, ok := doc["$date"]; ok {
136			switch v := jsonValue.(type) {
137			case string:
138				return util.FormatDate(v)
139			case bson.D:
140				asMap := v.Map()
141				if jsonValue, ok := asMap["$numberLong"]; ok {
142					n, err := parseNumberLongField(jsonValue)
143					if err != nil {
144						return nil, err
145					}
146					return time.Unix(n/1e3, n%1e3*1e6), err
147				}
148				return nil, errors.New("expected $numberLong field in $date")
149			case map[string]interface{}:
150				if jsonValue, ok := v["$numberLong"]; ok {
151					n, err := parseNumberLongField(jsonValue)
152					if err != nil {
153						return nil, err
154					}
155					return time.Unix(n/1e3, n%1e3*1e6), err
156				}
157				return nil, errors.New("expected $numberLong field in $date")
158
159			case json.Number:
160				n, err := v.Int64()
161				return time.Unix(n/1e3, n%1e3*1e6), err
162			case float64:
163				n := int64(v)
164				return time.Unix(n/1e3, n%1e3*1e6), nil
165			case int32:
166				n := int64(v)
167				return time.Unix(n/1e3, n%1e3*1e6), nil
168			case int64:
169				return time.Unix(v/1e3, v%1e3*1e6), nil
170
171			case json.ISODate:
172				return v, nil
173
174			default:
175				return nil, errors.New("invalid type for $date field")
176			}
177		}
178
179		if jsonValue, ok := doc["$code"]; ok {
180			switch v := jsonValue.(type) {
181			case string:
182				return primitive.JavaScript(v), nil
183			default:
184				return nil, errors.New("expected $code field to have string value")
185			}
186		}
187
188		if jsonValue, ok := doc["$oid"]; ok {
189			switch v := jsonValue.(type) {
190			case string:
191				return primitive.ObjectIDFromHex(v)
192			default:
193				return nil, errors.New("expected $oid field to have string value")
194			}
195		}
196
197		if jsonValue, ok := doc["$numberLong"]; ok {
198			return parseNumberLongField(jsonValue)
199		}
200
201		if jsonValue, ok := doc["$numberInt"]; ok {
202			switch v := jsonValue.(type) {
203			case string:
204				// all of decimal, hex, and octal are supported here
205				n, err := strconv.ParseInt(v, 0, 32)
206				return int32(n), err
207
208			default:
209				return nil, errors.New("expected $numberInt field to have string value")
210			}
211		}
212
213		if jsonValue, ok := doc["$timestamp"]; ok {
214			ts := json.Timestamp{}
215
216			var tsDoc map[string]interface{}
217			switch internalDoc := jsonValue.(type) {
218			case map[string]interface{}:
219				tsDoc = internalDoc
220			case bson.D:
221				tsDoc = internalDoc.Map()
222			default:
223				return nil, errors.New("expected $timestamp key to have internal document")
224			}
225
226			if seconds, ok := tsDoc["t"]; ok {
227				if asUint32, err := util.ToUInt32(seconds); err == nil {
228					ts.Seconds = asUint32
229				} else {
230					return nil, errors.New("expected $timestamp 't' field to be a numeric type")
231				}
232			} else {
233				return nil, errors.New("expected $timestamp to have 't' field")
234			}
235			if inc, ok := tsDoc["i"]; ok {
236				if asUint32, err := util.ToUInt32(inc); err == nil {
237					ts.Increment = asUint32
238				} else {
239					return nil, errors.New("expected $timestamp 'i' field to be  a numeric type")
240				}
241			} else {
242				return nil, errors.New("expected $timestamp to have 'i' field")
243			}
244			// see BSON spec for details on the bit fiddling here
245			return primitive.Timestamp{T: uint32(ts.Seconds), I: uint32(ts.Increment)}, nil
246		}
247
248		if jsonValue, ok := doc["$numberDecimal"]; ok {
249			switch v := jsonValue.(type) {
250			case string:
251				return primitive.ParseDecimal128(v)
252			default:
253				return nil, errors.New("expected $numberDecimal field to have string value")
254			}
255		}
256
257		if _, ok := doc["$undefined"]; ok {
258			return primitive.Undefined{}, nil
259		}
260
261		if _, ok := doc["$maxKey"]; ok {
262			return primitive.MaxKey{}, nil
263		}
264
265		if _, ok := doc["$minKey"]; ok {
266			return primitive.MinKey{}, nil
267		}
268
269	case 2: // document has two fields
270		if jsonValue, ok := doc["$code"]; ok {
271			code := primitive.CodeWithScope{}
272			switch v := jsonValue.(type) {
273			case string:
274				code.Code = primitive.JavaScript(v)
275			default:
276				return nil, errors.New("expected $code field to have string value")
277			}
278
279			if jsonValue, ok = doc["$scope"]; ok {
280				switch v2 := jsonValue.(type) {
281				case map[string]interface{}, bson.D:
282					x, err := ParseSpecialKeys(v2)
283					if err != nil {
284						return nil, err
285					}
286					code.Scope = x
287					return code, nil
288				default:
289					return nil, errors.New("expected $scope field to contain map")
290				}
291			} else {
292				return nil, errors.New("expected $scope field with $code field")
293			}
294		}
295
296		if jsonValue, ok := doc["$regex"]; ok {
297			regex := primitive.Regex{}
298
299			switch pattern := jsonValue.(type) {
300			case string:
301				regex.Pattern = pattern
302
303			default:
304				return nil, errors.New("expected $regex field to have string value")
305			}
306			if jsonValue, ok = doc["$options"]; !ok {
307				return nil, errors.New("expected $options field with $regex field")
308			}
309
310			switch options := jsonValue.(type) {
311			case string:
312				regex.Options = options
313
314			default:
315				return nil, errors.New("expected $options field to have string value")
316			}
317
318			// Validate regular expression options
319			for i := range regex.Options {
320				switch o := regex.Options[i]; o {
321				default:
322					return nil, fmt.Errorf("invalid regular expression option '%v'", o)
323
324				case 'g', 'i', 'm', 's': // allowed
325				}
326			}
327			return regex, nil
328		}
329
330		if jsonValue, ok := doc["$binary"]; ok {
331			binary := primitive.Binary{}
332
333			switch data := jsonValue.(type) {
334			case string:
335				bytes, err := base64.StdEncoding.DecodeString(data)
336				if err != nil {
337					return nil, err
338				}
339				binary.Data = bytes
340
341			default:
342				return nil, errors.New("expected $binary field to have string value")
343			}
344			if jsonValue, ok = doc["$type"]; !ok {
345				return nil, errors.New("expected $type field with $binary field")
346			}
347
348			switch typ := jsonValue.(type) {
349			case string:
350				kind, err := hex.DecodeString(typ)
351				if err != nil {
352					return nil, err
353				} else if len(kind) != 1 {
354					return nil, errors.New("expected single byte (as hexadecimal string) for $type field")
355				}
356				binary.Subtype = kind[0]
357
358			default:
359				return nil, errors.New("expected $type field to have string value")
360			}
361			return binary, nil
362		}
363	}
364
365	// nothing matched, so we recurse deeper
366	switch v := special.(type) {
367	case bson.D:
368		return GetExtendedBsonD(v)
369	case map[string]interface{}:
370		return ConvertLegacyExtJSONValueToBSON(v)
371	default:
372		return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special)
373	}
374}
375
376// ParseLegacyExtJSONValue takes any value generated by the json package and returns a
377// BSON version of that value.
378func ParseLegacyExtJSONValue(jsonValue interface{}) (interface{}, error) {
379	switch v := jsonValue.(type) {
380	case map[string]interface{}, bson.D: // subdocument
381		return ParseSpecialKeys(v)
382
383	default:
384		return ConvertLegacyExtJSONValueToBSON(v)
385	}
386}
387
388func parseNumberLongField(jsonValue interface{}) (int64, error) {
389	switch v := jsonValue.(type) {
390	case string:
391		// all of decimal, hex, and octal are supported here
392		return strconv.ParseInt(v, 0, 64)
393
394	default:
395		return 0, errors.New("expected $numberLong field to have string value")
396	}
397}
398