1268896Sbapt/*
2268896Sbapt * Copyright (c) 2014, Vsevolod Stakhov
3268896Sbapt *
4268896Sbapt * All rights reserved.
5268896Sbapt *
6268896Sbapt * Redistribution and use in source and binary forms, with or without
7268896Sbapt * modification, are permitted provided that the following conditions are met:
8268896Sbapt *	 * Redistributions of source code must retain the above copyright
9268896Sbapt *	   notice, this list of conditions and the following disclaimer.
10268896Sbapt *	 * Redistributions in binary form must reproduce the above copyright
11268896Sbapt *	   notice, this list of conditions and the following disclaimer in the
12268896Sbapt *	   documentation and/or other materials provided with the distribution.
13268896Sbapt *
14268896Sbapt * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
15268896Sbapt * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16268896Sbapt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17268896Sbapt * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
18268896Sbapt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19268896Sbapt * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20268896Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21268896Sbapt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22268896Sbapt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23268896Sbapt * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24268896Sbapt */
25268896Sbapt
26268896Sbapt#include "ucl.h"
27268896Sbapt#include "ucl_internal.h"
28268896Sbapt#include "tree.h"
29268896Sbapt#include "utlist.h"
30268896Sbapt#ifdef HAVE_STDARG_H
31268896Sbapt#include <stdarg.h>
32268896Sbapt#endif
33268896Sbapt#ifdef HAVE_STDIO_H
34268896Sbapt#include <stdio.h>
35268896Sbapt#endif
36268896Sbapt#ifdef HAVE_REGEX_H
37268896Sbapt#include <regex.h>
38268896Sbapt#endif
39268896Sbapt#ifdef HAVE_MATH_H
40268896Sbapt#include <math.h>
41268896Sbapt#endif
42268896Sbapt
43268896Sbaptstatic bool ucl_schema_validate (const ucl_object_t *schema,
44268896Sbapt		const ucl_object_t *obj, bool try_array,
45268896Sbapt		struct ucl_schema_error *err,
46268896Sbapt		const ucl_object_t *root);
47268896Sbapt
48268896Sbaptstatic bool
49268896Sbaptucl_string_to_type (const char *input, ucl_type_t *res)
50268896Sbapt{
51268896Sbapt	if (strcasecmp (input, "object") == 0) {
52268896Sbapt		*res = UCL_OBJECT;
53268896Sbapt	}
54268896Sbapt	else if (strcasecmp (input, "array") == 0) {
55268896Sbapt		*res = UCL_ARRAY;
56268896Sbapt	}
57268896Sbapt	else if (strcasecmp (input, "integer") == 0) {
58268896Sbapt		*res = UCL_INT;
59268896Sbapt	}
60268896Sbapt	else if (strcasecmp (input, "number") == 0) {
61268896Sbapt		*res = UCL_FLOAT;
62268896Sbapt	}
63268896Sbapt	else if (strcasecmp (input, "string") == 0) {
64268896Sbapt		*res = UCL_STRING;
65268896Sbapt	}
66268896Sbapt	else if (strcasecmp (input, "boolean") == 0) {
67268896Sbapt		*res = UCL_BOOLEAN;
68268896Sbapt	}
69268896Sbapt	else if (strcasecmp (input, "null") == 0) {
70268896Sbapt		*res = UCL_NULL;
71268896Sbapt	}
72268896Sbapt	else {
73268896Sbapt		return false;
74268896Sbapt	}
75268896Sbapt
76268896Sbapt	return true;
77268896Sbapt}
78268896Sbapt
79268896Sbaptstatic const char *
80268896Sbaptucl_object_type_to_string (ucl_type_t type)
81268896Sbapt{
82268896Sbapt	const char *res = "unknown";
83268896Sbapt
84268896Sbapt	switch (type) {
85268896Sbapt	case UCL_OBJECT:
86268896Sbapt		res = "object";
87268896Sbapt		break;
88268896Sbapt	case UCL_ARRAY:
89268896Sbapt		res = "array";
90268896Sbapt		break;
91268896Sbapt	case UCL_INT:
92268896Sbapt		res = "integer";
93268896Sbapt		break;
94268896Sbapt	case UCL_FLOAT:
95268896Sbapt	case UCL_TIME:
96268896Sbapt		res = "number";
97268896Sbapt		break;
98268896Sbapt	case UCL_STRING:
99268896Sbapt		res = "string";
100268896Sbapt		break;
101268896Sbapt	case UCL_BOOLEAN:
102268896Sbapt		res = "boolean";
103268896Sbapt		break;
104268896Sbapt	case UCL_NULL:
105268896Sbapt	case UCL_USERDATA:
106268896Sbapt		res = "null";
107268896Sbapt		break;
108268896Sbapt	}
109268896Sbapt
110268896Sbapt	return res;
111268896Sbapt}
112268896Sbapt
113268896Sbapt/*
114268896Sbapt * Create validation error
115268896Sbapt */
116268896Sbaptstatic void
117268896Sbaptucl_schema_create_error (struct ucl_schema_error *err,
118268896Sbapt		enum ucl_schema_error_code code, const ucl_object_t *obj,
119268896Sbapt		const char *fmt, ...)
120268896Sbapt{
121268896Sbapt	va_list va;
122268896Sbapt
123268896Sbapt	if (err != NULL) {
124268896Sbapt		err->code = code;
125268896Sbapt		err->obj = obj;
126268896Sbapt		va_start (va, fmt);
127268896Sbapt		vsnprintf (err->msg, sizeof (err->msg), fmt, va);
128268896Sbapt		va_end (va);
129268896Sbapt	}
130268896Sbapt}
131268896Sbapt
132268896Sbapt/*
133268896Sbapt * Check whether we have a pattern specified
134268896Sbapt */
135268896Sbaptstatic const ucl_object_t *
136268896Sbaptucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern)
137268896Sbapt{
138268896Sbapt	const ucl_object_t *res = NULL;
139268896Sbapt#ifdef HAVE_REGEX_H
140268896Sbapt	regex_t reg;
141268896Sbapt	const ucl_object_t *elt;
142268896Sbapt	ucl_object_iter_t iter = NULL;
143268896Sbapt
144268896Sbapt	if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
145268896Sbapt		while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
146268896Sbapt			if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
147268896Sbapt				res = elt;
148268896Sbapt				break;
149268896Sbapt			}
150268896Sbapt		}
151268896Sbapt		regfree (&reg);
152268896Sbapt	}
153268896Sbapt#endif
154268896Sbapt	return res;
155268896Sbapt}
156268896Sbapt
157268896Sbapt/*
158268896Sbapt * Check dependencies for an object
159268896Sbapt */
160268896Sbaptstatic bool
161268896Sbaptucl_schema_validate_dependencies (const ucl_object_t *deps,
162268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err,
163268896Sbapt		const ucl_object_t *root)
164268896Sbapt{
165268896Sbapt	const ucl_object_t *elt, *cur, *cur_dep;
166268896Sbapt	ucl_object_iter_t iter = NULL, piter;
167268896Sbapt	bool ret = true;
168268896Sbapt
169268896Sbapt	while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) {
170268896Sbapt		elt = ucl_object_find_key (obj, ucl_object_key (cur));
171268896Sbapt		if (elt != NULL) {
172268896Sbapt			/* Need to check dependencies */
173268896Sbapt			if (cur->type == UCL_ARRAY) {
174268896Sbapt				piter = NULL;
175268896Sbapt				while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) {
176268896Sbapt					if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) {
177268896Sbapt						ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
178268896Sbapt								"dependency %s is missing for key %s",
179268896Sbapt								ucl_object_tostring (cur_dep), ucl_object_key (cur));
180268896Sbapt						ret = false;
181268896Sbapt						break;
182268896Sbapt					}
183268896Sbapt				}
184268896Sbapt			}
185268896Sbapt			else if (cur->type == UCL_OBJECT) {
186268896Sbapt				ret = ucl_schema_validate (cur, obj, true, err, root);
187268896Sbapt			}
188268896Sbapt		}
189268896Sbapt	}
190268896Sbapt
191268896Sbapt	return ret;
192268896Sbapt}
193268896Sbapt
194268896Sbapt/*
195268896Sbapt * Validate object
196268896Sbapt */
197268896Sbaptstatic bool
198268896Sbaptucl_schema_validate_object (const ucl_object_t *schema,
199268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err,
200268896Sbapt		const ucl_object_t *root)
201268896Sbapt{
202268896Sbapt	const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
203268896Sbapt			*required = NULL, *pat, *pelt;
204268896Sbapt	ucl_object_iter_t iter = NULL, piter = NULL;
205268896Sbapt	bool ret = true, allow_additional = true;
206268896Sbapt	int64_t minmax;
207268896Sbapt
208268896Sbapt	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
209268896Sbapt		if (elt->type == UCL_OBJECT &&
210268896Sbapt				strcmp (ucl_object_key (elt), "properties") == 0) {
211268896Sbapt			piter = NULL;
212268896Sbapt			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
213268896Sbapt				found = ucl_object_find_key (obj, ucl_object_key (prop));
214268896Sbapt				if (found) {
215268896Sbapt					ret = ucl_schema_validate (prop, found, true, err, root);
216268896Sbapt				}
217268896Sbapt			}
218268896Sbapt		}
219268896Sbapt		else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
220268896Sbapt			if (elt->type == UCL_BOOLEAN) {
221268896Sbapt				if (!ucl_object_toboolean (elt)) {
222268896Sbapt					/* Deny additional fields completely */
223268896Sbapt					allow_additional = false;
224268896Sbapt				}
225268896Sbapt			}
226268896Sbapt			else if (elt->type == UCL_OBJECT) {
227268896Sbapt				/* Define validator for additional fields */
228268896Sbapt				additional_schema = elt;
229268896Sbapt			}
230268896Sbapt			else {
231268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
232268896Sbapt						"additionalProperties attribute is invalid in schema");
233268896Sbapt				ret = false;
234268896Sbapt				break;
235268896Sbapt			}
236268896Sbapt		}
237268896Sbapt		else if (strcmp (ucl_object_key (elt), "required") == 0) {
238268896Sbapt			if (elt->type == UCL_ARRAY) {
239268896Sbapt				required = elt;
240268896Sbapt			}
241268896Sbapt			else {
242268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
243268896Sbapt						"required attribute is invalid in schema");
244268896Sbapt				ret = false;
245268896Sbapt				break;
246268896Sbapt			}
247268896Sbapt		}
248268896Sbapt		else if (strcmp (ucl_object_key (elt), "minProperties") == 0
249268896Sbapt				&& ucl_object_toint_safe (elt, &minmax)) {
250268896Sbapt			if (obj->len < minmax) {
251268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
252268896Sbapt						"object has not enough properties: %u, minimum is: %u",
253268896Sbapt						obj->len, (unsigned)minmax);
254268896Sbapt				ret = false;
255268896Sbapt				break;
256268896Sbapt			}
257268896Sbapt		}
258268896Sbapt		else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
259268896Sbapt				&& ucl_object_toint_safe (elt, &minmax)) {
260268896Sbapt			if (obj->len > minmax) {
261268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
262268896Sbapt						"object has too many properties: %u, maximum is: %u",
263268896Sbapt						obj->len, (unsigned)minmax);
264268896Sbapt				ret = false;
265268896Sbapt				break;
266268896Sbapt			}
267268896Sbapt		}
268268896Sbapt		else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
269268896Sbapt			piter = NULL;
270268896Sbapt			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
271268896Sbapt				found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
272268896Sbapt				if (found) {
273268896Sbapt					ret = ucl_schema_validate (prop, found, true, err, root);
274268896Sbapt				}
275268896Sbapt			}
276268896Sbapt		}
277268896Sbapt		else if (elt->type == UCL_OBJECT &&
278268896Sbapt				strcmp (ucl_object_key (elt), "dependencies") == 0) {
279268896Sbapt			ret = ucl_schema_validate_dependencies (elt, obj, err, root);
280268896Sbapt		}
281268896Sbapt	}
282268896Sbapt
283268896Sbapt	if (ret) {
284268896Sbapt		/* Additional properties */
285268896Sbapt		if (!allow_additional || additional_schema != NULL) {
286268896Sbapt			/* Check if we have exactly the same properties in schema and object */
287268896Sbapt			iter = NULL;
288268896Sbapt			prop = ucl_object_find_key (schema, "properties");
289268896Sbapt			while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
290268896Sbapt				found = ucl_object_find_key (prop, ucl_object_key (elt));
291268896Sbapt				if (found == NULL) {
292268896Sbapt					/* Try patternProperties */
293268896Sbapt					piter = NULL;
294268896Sbapt					pat = ucl_object_find_key (schema, "patternProperties");
295268896Sbapt					while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) {
296268896Sbapt						found = ucl_schema_test_pattern (obj, ucl_object_key (pelt));
297268896Sbapt						if (found != NULL) {
298268896Sbapt							break;
299268896Sbapt						}
300268896Sbapt					}
301268896Sbapt				}
302268896Sbapt				if (found == NULL) {
303268896Sbapt					if (!allow_additional) {
304268896Sbapt						ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
305268896Sbapt								"object has non-allowed property %s",
306268896Sbapt								ucl_object_key (elt));
307268896Sbapt						ret = false;
308268896Sbapt						break;
309268896Sbapt					}
310268896Sbapt					else if (additional_schema != NULL) {
311268896Sbapt						if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
312268896Sbapt							ret = false;
313268896Sbapt							break;
314268896Sbapt						}
315268896Sbapt					}
316268896Sbapt				}
317268896Sbapt			}
318268896Sbapt		}
319268896Sbapt		/* Required properties */
320268896Sbapt		if (required != NULL) {
321268896Sbapt			iter = NULL;
322268896Sbapt			while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) {
323268896Sbapt				if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) {
324268896Sbapt					ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
325268896Sbapt							"object has missing property %s",
326268896Sbapt							ucl_object_tostring (elt));
327268896Sbapt					ret = false;
328268896Sbapt					break;
329268896Sbapt				}
330268896Sbapt			}
331268896Sbapt		}
332268896Sbapt	}
333268896Sbapt
334268896Sbapt
335268896Sbapt	return ret;
336268896Sbapt}
337268896Sbapt
338268896Sbaptstatic bool
339268896Sbaptucl_schema_validate_number (const ucl_object_t *schema,
340268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err)
341268896Sbapt{
342268896Sbapt	const ucl_object_t *elt, *test;
343268896Sbapt	ucl_object_iter_t iter = NULL;
344268896Sbapt	bool ret = true, exclusive = false;
345268896Sbapt	double constraint, val;
346268896Sbapt	const double alpha = 1e-16;
347268896Sbapt
348268896Sbapt	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
349268896Sbapt		if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
350268896Sbapt				strcmp (ucl_object_key (elt), "multipleOf") == 0) {
351268896Sbapt			constraint = ucl_object_todouble (elt);
352268896Sbapt			if (constraint <= 0) {
353268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
354268896Sbapt						"multipleOf must be greater than zero");
355268896Sbapt				ret = false;
356268896Sbapt				break;
357268896Sbapt			}
358268896Sbapt			val = ucl_object_todouble (obj);
359268896Sbapt			if (fabs (remainder (val, constraint)) > alpha) {
360268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
361268896Sbapt						"number %.4f is not multiple of %.4f, remainder is %.7f",
362268896Sbapt						val, constraint);
363268896Sbapt				ret = false;
364268896Sbapt				break;
365268896Sbapt			}
366268896Sbapt		}
367268896Sbapt		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
368268896Sbapt			strcmp (ucl_object_key (elt), "maximum") == 0) {
369268896Sbapt			constraint = ucl_object_todouble (elt);
370268896Sbapt			test = ucl_object_find_key (schema, "exclusiveMaximum");
371268896Sbapt			if (test && test->type == UCL_BOOLEAN) {
372268896Sbapt				exclusive = ucl_object_toboolean (test);
373268896Sbapt			}
374268896Sbapt			val = ucl_object_todouble (obj);
375268896Sbapt			if (val > constraint || (exclusive && val >= constraint)) {
376268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
377268896Sbapt						"number is too big: %.3f, maximum is: %.3f",
378268896Sbapt						val, constraint);
379268896Sbapt				ret = false;
380268896Sbapt				break;
381268896Sbapt			}
382268896Sbapt		}
383268896Sbapt		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
384268896Sbapt				strcmp (ucl_object_key (elt), "minimum") == 0) {
385268896Sbapt			constraint = ucl_object_todouble (elt);
386268896Sbapt			test = ucl_object_find_key (schema, "exclusiveMinimum");
387268896Sbapt			if (test && test->type == UCL_BOOLEAN) {
388268896Sbapt				exclusive = ucl_object_toboolean (test);
389268896Sbapt			}
390268896Sbapt			val = ucl_object_todouble (obj);
391268896Sbapt			if (val < constraint || (exclusive && val <= constraint)) {
392268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
393268896Sbapt						"number is too small: %.3f, minimum is: %.3f",
394268896Sbapt						val, constraint);
395268896Sbapt				ret = false;
396268896Sbapt				break;
397268896Sbapt			}
398268896Sbapt		}
399268896Sbapt	}
400268896Sbapt
401268896Sbapt	return ret;
402268896Sbapt}
403268896Sbapt
404268896Sbaptstatic bool
405268896Sbaptucl_schema_validate_string (const ucl_object_t *schema,
406268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err)
407268896Sbapt{
408268896Sbapt	const ucl_object_t *elt;
409268896Sbapt	ucl_object_iter_t iter = NULL;
410268896Sbapt	bool ret = true;
411268896Sbapt	int64_t constraint;
412268896Sbapt#ifdef HAVE_REGEX_H
413268896Sbapt	regex_t re;
414268896Sbapt#endif
415268896Sbapt
416268896Sbapt	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
417268896Sbapt		if (elt->type == UCL_INT &&
418268896Sbapt			strcmp (ucl_object_key (elt), "maxLength") == 0) {
419268896Sbapt			constraint = ucl_object_toint (elt);
420268896Sbapt			if (obj->len > constraint) {
421268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
422268896Sbapt						"string is too big: %.3f, maximum is: %.3f",
423268896Sbapt						obj->len, constraint);
424268896Sbapt				ret = false;
425268896Sbapt				break;
426268896Sbapt			}
427268896Sbapt		}
428268896Sbapt		else if (elt->type == UCL_INT &&
429268896Sbapt				strcmp (ucl_object_key (elt), "minLength") == 0) {
430268896Sbapt			constraint = ucl_object_toint (elt);
431268896Sbapt			if (obj->len < constraint) {
432268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
433268896Sbapt						"string is too short: %.3f, minimum is: %.3f",
434268896Sbapt						obj->len, constraint);
435268896Sbapt				ret = false;
436268896Sbapt				break;
437268896Sbapt			}
438268896Sbapt		}
439268896Sbapt#ifdef HAVE_REGEX_H
440268896Sbapt		else if (elt->type == UCL_STRING &&
441268896Sbapt				strcmp (ucl_object_key (elt), "pattern") == 0) {
442268896Sbapt			if (regcomp (&re, ucl_object_tostring (elt),
443268896Sbapt					REG_EXTENDED | REG_NOSUB) != 0) {
444268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
445268896Sbapt						"cannot compile pattern %s", ucl_object_tostring (elt));
446268896Sbapt				ret = false;
447268896Sbapt				break;
448268896Sbapt			}
449268896Sbapt			if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
450268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
451268896Sbapt						"string doesn't match regexp %s",
452268896Sbapt						ucl_object_tostring (elt));
453268896Sbapt				ret = false;
454268896Sbapt			}
455268896Sbapt			regfree (&re);
456268896Sbapt		}
457268896Sbapt#endif
458268896Sbapt	}
459268896Sbapt
460268896Sbapt	return ret;
461268896Sbapt}
462268896Sbapt
463268896Sbaptstruct ucl_compare_node {
464268896Sbapt	const ucl_object_t *obj;
465268896Sbapt	TREE_ENTRY(ucl_compare_node) link;
466268896Sbapt	struct ucl_compare_node *next;
467268896Sbapt};
468268896Sbapt
469268896Sbapttypedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
470268896Sbapt
471268896SbaptTREE_DEFINE(ucl_compare_node, link)
472268896Sbapt
473268896Sbaptstatic int
474268896Sbaptucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
475268896Sbapt{
476268896Sbapt	const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
477268896Sbapt
478268896Sbapt	return ucl_object_compare (o1, o2);
479268896Sbapt}
480268896Sbapt
481268896Sbaptstatic bool
482268896Sbaptucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
483268896Sbapt{
484268896Sbapt	ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
485268896Sbapt	ucl_object_iter_t iter = NULL;
486268896Sbapt	const ucl_object_t *elt;
487268896Sbapt	struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
488268896Sbapt	bool ret = true;
489268896Sbapt
490268896Sbapt	while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
491268896Sbapt		test.obj = elt;
492268896Sbapt		node = TREE_FIND (&tree, ucl_compare_node, link, &test);
493268896Sbapt		if (node != NULL) {
494268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
495268896Sbapt					"duplicate values detected while uniqueItems is true");
496268896Sbapt			ret = false;
497268896Sbapt			break;
498268896Sbapt		}
499268896Sbapt		node = calloc (1, sizeof (*node));
500268896Sbapt		if (node == NULL) {
501268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
502268896Sbapt					"cannot allocate tree node");
503268896Sbapt			ret = false;
504268896Sbapt			break;
505268896Sbapt		}
506268896Sbapt		node->obj = elt;
507268896Sbapt		TREE_INSERT (&tree, ucl_compare_node, link, node);
508268896Sbapt		LL_PREPEND (nodes, node);
509268896Sbapt	}
510268896Sbapt
511268896Sbapt	LL_FOREACH_SAFE (nodes, node, tmp) {
512268896Sbapt		free (node);
513268896Sbapt	}
514268896Sbapt
515268896Sbapt	return ret;
516268896Sbapt}
517268896Sbapt
518268896Sbaptstatic bool
519268896Sbaptucl_schema_validate_array (const ucl_object_t *schema,
520268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err,
521268896Sbapt		const ucl_object_t *root)
522268896Sbapt{
523268896Sbapt	const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
524268896Sbapt			*first_unvalidated = NULL;
525268896Sbapt	ucl_object_iter_t iter = NULL, piter = NULL;
526268896Sbapt	bool ret = true, allow_additional = true, need_unique = false;
527268896Sbapt	int64_t minmax;
528268896Sbapt
529268896Sbapt	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
530268896Sbapt		if (strcmp (ucl_object_key (elt), "items") == 0) {
531268896Sbapt			if (elt->type == UCL_ARRAY) {
532268896Sbapt				found = obj->value.av;
533268896Sbapt				while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
534268896Sbapt					if (found) {
535268896Sbapt						ret = ucl_schema_validate (it, found, false, err, root);
536268896Sbapt						found = found->next;
537268896Sbapt					}
538268896Sbapt				}
539268896Sbapt				if (found != NULL) {
540268896Sbapt					/* The first element that is not validated */
541268896Sbapt					first_unvalidated = found;
542268896Sbapt				}
543268896Sbapt			}
544268896Sbapt			else if (elt->type == UCL_OBJECT) {
545268896Sbapt				/* Validate all items using the specified schema */
546268896Sbapt				while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
547268896Sbapt					ret = ucl_schema_validate (elt, it, false, err, root);
548268896Sbapt				}
549268896Sbapt			}
550268896Sbapt			else {
551268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
552268896Sbapt						"items attribute is invalid in schema");
553268896Sbapt				ret = false;
554268896Sbapt				break;
555268896Sbapt			}
556268896Sbapt		}
557268896Sbapt		else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
558268896Sbapt			if (elt->type == UCL_BOOLEAN) {
559268896Sbapt				if (!ucl_object_toboolean (elt)) {
560268896Sbapt					/* Deny additional fields completely */
561268896Sbapt					allow_additional = false;
562268896Sbapt				}
563268896Sbapt			}
564268896Sbapt			else if (elt->type == UCL_OBJECT) {
565268896Sbapt				/* Define validator for additional fields */
566268896Sbapt				additional_schema = elt;
567268896Sbapt			}
568268896Sbapt			else {
569268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
570268896Sbapt						"additionalItems attribute is invalid in schema");
571268896Sbapt				ret = false;
572268896Sbapt				break;
573268896Sbapt			}
574268896Sbapt		}
575268896Sbapt		else if (elt->type == UCL_BOOLEAN &&
576268896Sbapt				strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
577268896Sbapt			need_unique = ucl_object_toboolean (elt);
578268896Sbapt		}
579268896Sbapt		else if (strcmp (ucl_object_key (elt), "minItems") == 0
580268896Sbapt				&& ucl_object_toint_safe (elt, &minmax)) {
581268896Sbapt			if (obj->len < minmax) {
582268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
583268896Sbapt						"array has not enough items: %u, minimum is: %u",
584268896Sbapt						obj->len, (unsigned)minmax);
585268896Sbapt				ret = false;
586268896Sbapt				break;
587268896Sbapt			}
588268896Sbapt		}
589268896Sbapt		else if (strcmp (ucl_object_key (elt), "maxItems") == 0
590268896Sbapt				&& ucl_object_toint_safe (elt, &minmax)) {
591268896Sbapt			if (obj->len > minmax) {
592268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
593268896Sbapt						"array has too many items: %u, maximum is: %u",
594268896Sbapt						obj->len, (unsigned)minmax);
595268896Sbapt				ret = false;
596268896Sbapt				break;
597268896Sbapt			}
598268896Sbapt		}
599268896Sbapt	}
600268896Sbapt
601268896Sbapt	if (ret) {
602268896Sbapt		/* Additional properties */
603268896Sbapt		if (!allow_additional || additional_schema != NULL) {
604268896Sbapt			if (first_unvalidated != NULL) {
605268896Sbapt				if (!allow_additional) {
606268896Sbapt					ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
607268896Sbapt							"array has undefined item");
608268896Sbapt					ret = false;
609268896Sbapt				}
610268896Sbapt				else if (additional_schema != NULL) {
611268896Sbapt					elt = first_unvalidated;
612268896Sbapt					while (elt) {
613268896Sbapt						if (!ucl_schema_validate (additional_schema, elt, false,
614268896Sbapt								err, root)) {
615268896Sbapt							ret = false;
616268896Sbapt							break;
617268896Sbapt						}
618268896Sbapt						elt = elt->next;
619268896Sbapt					}
620268896Sbapt				}
621268896Sbapt			}
622268896Sbapt		}
623268896Sbapt		/* Required properties */
624268896Sbapt		if (ret && need_unique) {
625268896Sbapt			ret = ucl_schema_array_is_unique (obj, err);
626268896Sbapt		}
627268896Sbapt	}
628268896Sbapt
629268896Sbapt	return ret;
630268896Sbapt}
631268896Sbapt
632268896Sbapt/*
633268896Sbapt * Returns whether this object is allowed for this type
634268896Sbapt */
635268896Sbaptstatic bool
636268896Sbaptucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
637268896Sbapt		struct ucl_schema_error *err)
638268896Sbapt{
639268896Sbapt	ucl_object_iter_t iter = NULL;
640268896Sbapt	const ucl_object_t *elt;
641268896Sbapt	const char *type_str;
642268896Sbapt	ucl_type_t t;
643268896Sbapt
644268896Sbapt	if (type == NULL) {
645268896Sbapt		/* Any type is allowed */
646268896Sbapt		return true;
647268896Sbapt	}
648268896Sbapt
649268896Sbapt	if (type->type == UCL_ARRAY) {
650268896Sbapt		/* One of allowed types */
651268896Sbapt		while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) {
652268896Sbapt			if (ucl_schema_type_is_allowed (elt, obj, err)) {
653268896Sbapt				return true;
654268896Sbapt			}
655268896Sbapt		}
656268896Sbapt	}
657268896Sbapt	else if (type->type == UCL_STRING) {
658268896Sbapt		type_str = ucl_object_tostring (type);
659268896Sbapt		if (!ucl_string_to_type (type_str, &t)) {
660268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
661268896Sbapt					"Type attribute is invalid in schema");
662268896Sbapt			return false;
663268896Sbapt		}
664268896Sbapt		if (obj->type != t) {
665268896Sbapt			/* Some types are actually compatible */
666268896Sbapt			if (obj->type == UCL_TIME && t == UCL_FLOAT) {
667268896Sbapt				return true;
668268896Sbapt			}
669268896Sbapt			else if (obj->type == UCL_INT && t == UCL_FLOAT) {
670268896Sbapt				return true;
671268896Sbapt			}
672268896Sbapt			else {
673268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
674268896Sbapt						"Invalid type of %s, expected %s",
675268896Sbapt						ucl_object_type_to_string (obj->type),
676268896Sbapt						ucl_object_type_to_string (t));
677268896Sbapt			}
678268896Sbapt		}
679268896Sbapt		else {
680268896Sbapt			/* Types are equal */
681268896Sbapt			return true;
682268896Sbapt		}
683268896Sbapt	}
684268896Sbapt
685268896Sbapt	return false;
686268896Sbapt}
687268896Sbapt
688268896Sbapt/*
689268896Sbapt * Check if object is equal to one of elements of enum
690268896Sbapt */
691268896Sbaptstatic bool
692268896Sbaptucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
693268896Sbapt		struct ucl_schema_error *err)
694268896Sbapt{
695268896Sbapt	ucl_object_iter_t iter = NULL;
696268896Sbapt	const ucl_object_t *elt;
697268896Sbapt	bool ret = false;
698268896Sbapt
699268896Sbapt	while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) {
700268896Sbapt		if (ucl_object_compare (elt, obj) == 0) {
701268896Sbapt			ret = true;
702268896Sbapt			break;
703268896Sbapt		}
704268896Sbapt	}
705268896Sbapt
706268896Sbapt	if (!ret) {
707268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
708268896Sbapt				"object is not one of enumerated patterns");
709268896Sbapt	}
710268896Sbapt
711268896Sbapt	return ret;
712268896Sbapt}
713268896Sbapt
714268896Sbapt
715268896Sbapt/*
716268896Sbapt * Check a single ref component
717268896Sbapt */
718268896Sbaptstatic const ucl_object_t *
719268896Sbaptucl_schema_resolve_ref_component (const ucl_object_t *cur,
720268896Sbapt		const char *refc, int len,
721268896Sbapt		struct ucl_schema_error *err)
722268896Sbapt{
723268896Sbapt	const ucl_object_t *res = NULL;
724268896Sbapt	char *err_str;
725268896Sbapt	int num, i;
726268896Sbapt
727268896Sbapt	if (cur->type == UCL_OBJECT) {
728268896Sbapt		/* Find a key inside an object */
729268896Sbapt		res = ucl_object_find_keyl (cur, refc, len);
730268896Sbapt		if (res == NULL) {
731268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
732268896Sbapt					"reference %s is invalid, missing path component", refc);
733268896Sbapt			return NULL;
734268896Sbapt		}
735268896Sbapt	}
736268896Sbapt	else if (cur->type == UCL_ARRAY) {
737268896Sbapt		/* We must figure out a number inside array */
738268896Sbapt		num = strtoul (refc, &err_str, 10);
739268896Sbapt		if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
740268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
741268896Sbapt					"reference %s is invalid, invalid item number", refc);
742268896Sbapt			return NULL;
743268896Sbapt		}
744268896Sbapt		res = cur->value.av;
745268896Sbapt		i = 0;
746268896Sbapt		while (res != NULL) {
747268896Sbapt			if (i == num) {
748268896Sbapt				break;
749268896Sbapt			}
750268896Sbapt			res = res->next;
751268896Sbapt		}
752268896Sbapt		if (res == NULL) {
753268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
754268896Sbapt					"reference %s is invalid, item number %d does not exist",
755268896Sbapt					refc, num);
756268896Sbapt			return NULL;
757268896Sbapt		}
758268896Sbapt	}
759268896Sbapt	else {
760268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
761268896Sbapt				"reference %s is invalid, contains primitive object in the path",
762268896Sbapt				refc);
763268896Sbapt		return NULL;
764268896Sbapt	}
765268896Sbapt
766268896Sbapt	return res;
767268896Sbapt}
768268896Sbapt/*
769268896Sbapt * Find reference schema
770268896Sbapt */
771268896Sbaptstatic const ucl_object_t *
772268896Sbaptucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
773268896Sbapt		struct ucl_schema_error *err)
774268896Sbapt{
775268896Sbapt	const char *p, *c;
776268896Sbapt	const ucl_object_t *res = NULL;
777268896Sbapt
778268896Sbapt
779268896Sbapt	if (ref[0] != '#') {
780268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
781268896Sbapt				"reference %s is invalid, not started with #", ref);
782268896Sbapt		return NULL;
783268896Sbapt	}
784268896Sbapt	if (ref[1] == '/') {
785268896Sbapt		p = &ref[2];
786268896Sbapt	}
787268896Sbapt	else if (ref[1] == '\0') {
788268896Sbapt		return root;
789268896Sbapt	}
790268896Sbapt	else {
791268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
792268896Sbapt				"reference %s is invalid, not started with #/", ref);
793268896Sbapt		return NULL;
794268896Sbapt	}
795268896Sbapt
796268896Sbapt	c = p;
797268896Sbapt	res = root;
798268896Sbapt
799268896Sbapt	while (*p != '\0') {
800268896Sbapt		if (*p == '/') {
801268896Sbapt			if (p - c == 0) {
802268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
803268896Sbapt						"reference %s is invalid, empty path component", ref);
804268896Sbapt				return NULL;
805268896Sbapt			}
806268896Sbapt			/* Now we have some url part, so we need to figure out where we are */
807268896Sbapt			res = ucl_schema_resolve_ref_component (res, c, p - c, err);
808268896Sbapt			if (res == NULL) {
809268896Sbapt				return NULL;
810268896Sbapt			}
811268896Sbapt			c = p + 1;
812268896Sbapt		}
813268896Sbapt		p ++;
814268896Sbapt	}
815268896Sbapt
816268896Sbapt	if (p - c != 0) {
817268896Sbapt		res = ucl_schema_resolve_ref_component (res, c, p - c, err);
818268896Sbapt	}
819268896Sbapt
820268896Sbapt	if (res == NULL || res->type != UCL_OBJECT) {
821268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
822268896Sbapt				"reference %s is invalid, cannot find specified object",
823268896Sbapt				ref);
824268896Sbapt		return NULL;
825268896Sbapt	}
826268896Sbapt
827268896Sbapt	return res;
828268896Sbapt}
829268896Sbapt
830268896Sbaptstatic bool
831268896Sbaptucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
832268896Sbapt		struct ucl_schema_error *err)
833268896Sbapt{
834268896Sbapt	const ucl_object_t *elt, *cur;
835268896Sbapt	int64_t constraint, i;
836268896Sbapt
837268896Sbapt	elt = ucl_object_find_key (schema, "maxValues");
838268896Sbapt	if (elt != NULL && elt->type == UCL_INT) {
839268896Sbapt		constraint = ucl_object_toint (elt);
840268896Sbapt		cur = obj;
841268896Sbapt		i = 0;
842268896Sbapt		while (cur) {
843268896Sbapt			if (i > constraint) {
844268896Sbapt				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
845268896Sbapt					"object has more values than defined: %ld",
846268896Sbapt					(long int)constraint);
847268896Sbapt				return false;
848268896Sbapt			}
849268896Sbapt			i ++;
850268896Sbapt			cur = cur->next;
851268896Sbapt		}
852268896Sbapt	}
853268896Sbapt	elt = ucl_object_find_key (schema, "minValues");
854268896Sbapt	if (elt != NULL && elt->type == UCL_INT) {
855268896Sbapt		constraint = ucl_object_toint (elt);
856268896Sbapt		cur = obj;
857268896Sbapt		i = 0;
858268896Sbapt		while (cur) {
859268896Sbapt			if (i >= constraint) {
860268896Sbapt				break;
861268896Sbapt			}
862268896Sbapt			i ++;
863268896Sbapt			cur = cur->next;
864268896Sbapt		}
865268896Sbapt		if (i < constraint) {
866268896Sbapt			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
867268896Sbapt					"object has less values than defined: %ld",
868268896Sbapt					(long int)constraint);
869268896Sbapt			return false;
870268896Sbapt		}
871268896Sbapt	}
872268896Sbapt
873268896Sbapt	return true;
874268896Sbapt}
875268896Sbapt
876268896Sbaptstatic bool
877268896Sbaptucl_schema_validate (const ucl_object_t *schema,
878268896Sbapt		const ucl_object_t *obj, bool try_array,
879268896Sbapt		struct ucl_schema_error *err,
880268896Sbapt		const ucl_object_t *root)
881268896Sbapt{
882268896Sbapt	const ucl_object_t *elt, *cur;
883268896Sbapt	ucl_object_iter_t iter = NULL;
884268896Sbapt	bool ret;
885268896Sbapt
886268896Sbapt	if (schema->type != UCL_OBJECT) {
887268896Sbapt		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
888268896Sbapt				"schema is %s instead of object", ucl_object_type_to_string (schema->type));
889268896Sbapt		return false;
890268896Sbapt	}
891268896Sbapt
892268896Sbapt	if (try_array) {
893268896Sbapt		/*
894268896Sbapt		 * Special case for multiple values
895268896Sbapt		 */
896268896Sbapt		if (!ucl_schema_validate_values (schema, obj, err)) {
897268896Sbapt			return false;
898268896Sbapt		}
899268896Sbapt		LL_FOREACH (obj, cur) {
900268896Sbapt			if (!ucl_schema_validate (schema, cur, false, err, root)) {
901268896Sbapt				return false;
902268896Sbapt			}
903268896Sbapt		}
904268896Sbapt		return true;
905268896Sbapt	}
906268896Sbapt
907268896Sbapt	elt = ucl_object_find_key (schema, "enum");
908268896Sbapt	if (elt != NULL && elt->type == UCL_ARRAY) {
909268896Sbapt		if (!ucl_schema_validate_enum (elt, obj, err)) {
910268896Sbapt			return false;
911268896Sbapt		}
912268896Sbapt	}
913268896Sbapt
914268896Sbapt	elt = ucl_object_find_key (schema, "allOf");
915268896Sbapt	if (elt != NULL && elt->type == UCL_ARRAY) {
916268896Sbapt		iter = NULL;
917268896Sbapt		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
918268896Sbapt			ret = ucl_schema_validate (cur, obj, true, err, root);
919268896Sbapt			if (!ret) {
920268896Sbapt				return false;
921268896Sbapt			}
922268896Sbapt		}
923268896Sbapt	}
924268896Sbapt
925268896Sbapt	elt = ucl_object_find_key (schema, "anyOf");
926268896Sbapt	if (elt != NULL && elt->type == UCL_ARRAY) {
927268896Sbapt		iter = NULL;
928268896Sbapt		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
929268896Sbapt			ret = ucl_schema_validate (cur, obj, true, err, root);
930268896Sbapt			if (ret) {
931268896Sbapt				break;
932268896Sbapt			}
933268896Sbapt		}
934268896Sbapt		if (!ret) {
935268896Sbapt			return false;
936268896Sbapt		}
937268896Sbapt		else {
938268896Sbapt			/* Reset error */
939268896Sbapt			err->code = UCL_SCHEMA_OK;
940268896Sbapt		}
941268896Sbapt	}
942268896Sbapt
943268896Sbapt	elt = ucl_object_find_key (schema, "oneOf");
944268896Sbapt	if (elt != NULL && elt->type == UCL_ARRAY) {
945268896Sbapt		iter = NULL;
946268896Sbapt		ret = false;
947268896Sbapt		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
948268896Sbapt			if (!ret) {
949268896Sbapt				ret = ucl_schema_validate (cur, obj, true, err, root);
950268896Sbapt			}
951268896Sbapt			else if (ucl_schema_validate (cur, obj, true, err, root)) {
952268896Sbapt				ret = false;
953268896Sbapt				break;
954268896Sbapt			}
955268896Sbapt		}
956268896Sbapt		if (!ret) {
957268896Sbapt			return false;
958268896Sbapt		}
959268896Sbapt	}
960268896Sbapt
961268896Sbapt	elt = ucl_object_find_key (schema, "not");
962268896Sbapt	if (elt != NULL && elt->type == UCL_OBJECT) {
963268896Sbapt		if (ucl_schema_validate (elt, obj, true, err, root)) {
964268896Sbapt			return false;
965268896Sbapt		}
966268896Sbapt		else {
967268896Sbapt			/* Reset error */
968268896Sbapt			err->code = UCL_SCHEMA_OK;
969268896Sbapt		}
970268896Sbapt	}
971268896Sbapt
972268896Sbapt	elt = ucl_object_find_key (schema, "$ref");
973268896Sbapt	if (elt != NULL) {
974268896Sbapt		cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
975268896Sbapt		if (cur == NULL) {
976268896Sbapt			return false;
977268896Sbapt		}
978268896Sbapt		if (!ucl_schema_validate (cur, obj, try_array, err, root)) {
979268896Sbapt			return false;
980268896Sbapt		}
981268896Sbapt	}
982268896Sbapt
983268896Sbapt	elt = ucl_object_find_key (schema, "type");
984268896Sbapt	if (!ucl_schema_type_is_allowed (elt, obj, err)) {
985268896Sbapt		return false;
986268896Sbapt	}
987268896Sbapt
988268896Sbapt	switch (obj->type) {
989268896Sbapt	case UCL_OBJECT:
990268896Sbapt		return ucl_schema_validate_object (schema, obj, err, root);
991268896Sbapt		break;
992268896Sbapt	case UCL_ARRAY:
993268896Sbapt		return ucl_schema_validate_array (schema, obj, err, root);
994268896Sbapt		break;
995268896Sbapt	case UCL_INT:
996268896Sbapt	case UCL_FLOAT:
997268896Sbapt		return ucl_schema_validate_number (schema, obj, err);
998268896Sbapt		break;
999268896Sbapt	case UCL_STRING:
1000268896Sbapt		return ucl_schema_validate_string (schema, obj, err);
1001268896Sbapt		break;
1002268896Sbapt	default:
1003268896Sbapt		break;
1004268896Sbapt	}
1005268896Sbapt
1006268896Sbapt	return true;
1007268896Sbapt}
1008268896Sbapt
1009268896Sbaptbool
1010268896Sbaptucl_object_validate (const ucl_object_t *schema,
1011268896Sbapt		const ucl_object_t *obj, struct ucl_schema_error *err)
1012268896Sbapt{
1013268896Sbapt	return ucl_schema_validate (schema, obj, true, err, schema);
1014268896Sbapt}
1015