xref: /freebsd/contrib/libucl/src/ucl_schema.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*
2  * Copyright (c) 2014, Vsevolod Stakhov
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *	 * Redistributions of source code must retain the above copyright
9  *	   notice, this list of conditions and the following disclaimer.
10  *	 * Redistributions in binary form must reproduce the above copyright
11  *	   notice, this list of conditions and the following disclaimer in the
12  *	   documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "ucl.h"
27 #include "ucl_internal.h"
28 #include "tree.h"
29 #include "utlist.h"
30 #ifdef HAVE_STDARG_H
31 #include <stdarg.h>
32 #endif
33 #ifdef HAVE_STDIO_H
34 #include <stdio.h>
35 #endif
36 #ifdef HAVE_REGEX_H
37 #include <regex.h>
38 #endif
39 #ifdef HAVE_MATH_H
40 #include <math.h>
41 #endif
42 
43 static bool ucl_schema_validate (const ucl_object_t *schema,
44 		const ucl_object_t *obj, bool try_array,
45 		struct ucl_schema_error *err,
46 		const ucl_object_t *root);
47 
48 static bool
49 ucl_string_to_type (const char *input, ucl_type_t *res)
50 {
51 	if (strcasecmp (input, "object") == 0) {
52 		*res = UCL_OBJECT;
53 	}
54 	else if (strcasecmp (input, "array") == 0) {
55 		*res = UCL_ARRAY;
56 	}
57 	else if (strcasecmp (input, "integer") == 0) {
58 		*res = UCL_INT;
59 	}
60 	else if (strcasecmp (input, "number") == 0) {
61 		*res = UCL_FLOAT;
62 	}
63 	else if (strcasecmp (input, "string") == 0) {
64 		*res = UCL_STRING;
65 	}
66 	else if (strcasecmp (input, "boolean") == 0) {
67 		*res = UCL_BOOLEAN;
68 	}
69 	else if (strcasecmp (input, "null") == 0) {
70 		*res = UCL_NULL;
71 	}
72 	else {
73 		return false;
74 	}
75 
76 	return true;
77 }
78 
79 static const char *
80 ucl_object_type_to_string (ucl_type_t type)
81 {
82 	const char *res = "unknown";
83 
84 	switch (type) {
85 	case UCL_OBJECT:
86 		res = "object";
87 		break;
88 	case UCL_ARRAY:
89 		res = "array";
90 		break;
91 	case UCL_INT:
92 		res = "integer";
93 		break;
94 	case UCL_FLOAT:
95 	case UCL_TIME:
96 		res = "number";
97 		break;
98 	case UCL_STRING:
99 		res = "string";
100 		break;
101 	case UCL_BOOLEAN:
102 		res = "boolean";
103 		break;
104 	case UCL_NULL:
105 	case UCL_USERDATA:
106 		res = "null";
107 		break;
108 	}
109 
110 	return res;
111 }
112 
113 /*
114  * Create validation error
115  */
116 static void
117 ucl_schema_create_error (struct ucl_schema_error *err,
118 		enum ucl_schema_error_code code, const ucl_object_t *obj,
119 		const char *fmt, ...)
120 {
121 	va_list va;
122 
123 	if (err != NULL) {
124 		err->code = code;
125 		err->obj = obj;
126 		va_start (va, fmt);
127 		vsnprintf (err->msg, sizeof (err->msg), fmt, va);
128 		va_end (va);
129 	}
130 }
131 
132 /*
133  * Check whether we have a pattern specified
134  */
135 static const ucl_object_t *
136 ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern)
137 {
138 	const ucl_object_t *res = NULL;
139 #ifdef HAVE_REGEX_H
140 	regex_t reg;
141 	const ucl_object_t *elt;
142 	ucl_object_iter_t iter = NULL;
143 
144 	if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
145 		while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
146 			if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
147 				res = elt;
148 				break;
149 			}
150 		}
151 		regfree (&reg);
152 	}
153 #endif
154 	return res;
155 }
156 
157 /*
158  * Check dependencies for an object
159  */
160 static bool
161 ucl_schema_validate_dependencies (const ucl_object_t *deps,
162 		const ucl_object_t *obj, struct ucl_schema_error *err,
163 		const ucl_object_t *root)
164 {
165 	const ucl_object_t *elt, *cur, *cur_dep;
166 	ucl_object_iter_t iter = NULL, piter;
167 	bool ret = true;
168 
169 	while (ret && (cur = ucl_iterate_object (deps, &iter, true)) != NULL) {
170 		elt = ucl_object_find_key (obj, ucl_object_key (cur));
171 		if (elt != NULL) {
172 			/* Need to check dependencies */
173 			if (cur->type == UCL_ARRAY) {
174 				piter = NULL;
175 				while (ret && (cur_dep = ucl_iterate_object (cur, &piter, true)) != NULL) {
176 					if (ucl_object_find_key (obj, ucl_object_tostring (cur_dep)) == NULL) {
177 						ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
178 								"dependency %s is missing for key %s",
179 								ucl_object_tostring (cur_dep), ucl_object_key (cur));
180 						ret = false;
181 						break;
182 					}
183 				}
184 			}
185 			else if (cur->type == UCL_OBJECT) {
186 				ret = ucl_schema_validate (cur, obj, true, err, root);
187 			}
188 		}
189 	}
190 
191 	return ret;
192 }
193 
194 /*
195  * Validate object
196  */
197 static bool
198 ucl_schema_validate_object (const ucl_object_t *schema,
199 		const ucl_object_t *obj, struct ucl_schema_error *err,
200 		const ucl_object_t *root)
201 {
202 	const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
203 			*required = NULL, *pat, *pelt;
204 	ucl_object_iter_t iter = NULL, piter = NULL;
205 	bool ret = true, allow_additional = true;
206 	int64_t minmax;
207 
208 	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
209 		if (elt->type == UCL_OBJECT &&
210 				strcmp (ucl_object_key (elt), "properties") == 0) {
211 			piter = NULL;
212 			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
213 				found = ucl_object_find_key (obj, ucl_object_key (prop));
214 				if (found) {
215 					ret = ucl_schema_validate (prop, found, true, err, root);
216 				}
217 			}
218 		}
219 		else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
220 			if (elt->type == UCL_BOOLEAN) {
221 				if (!ucl_object_toboolean (elt)) {
222 					/* Deny additional fields completely */
223 					allow_additional = false;
224 				}
225 			}
226 			else if (elt->type == UCL_OBJECT) {
227 				/* Define validator for additional fields */
228 				additional_schema = elt;
229 			}
230 			else {
231 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
232 						"additionalProperties attribute is invalid in schema");
233 				ret = false;
234 				break;
235 			}
236 		}
237 		else if (strcmp (ucl_object_key (elt), "required") == 0) {
238 			if (elt->type == UCL_ARRAY) {
239 				required = elt;
240 			}
241 			else {
242 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
243 						"required attribute is invalid in schema");
244 				ret = false;
245 				break;
246 			}
247 		}
248 		else if (strcmp (ucl_object_key (elt), "minProperties") == 0
249 				&& ucl_object_toint_safe (elt, &minmax)) {
250 			if (obj->len < minmax) {
251 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
252 						"object has not enough properties: %u, minimum is: %u",
253 						obj->len, (unsigned)minmax);
254 				ret = false;
255 				break;
256 			}
257 		}
258 		else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
259 				&& ucl_object_toint_safe (elt, &minmax)) {
260 			if (obj->len > minmax) {
261 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
262 						"object has too many properties: %u, maximum is: %u",
263 						obj->len, (unsigned)minmax);
264 				ret = false;
265 				break;
266 			}
267 		}
268 		else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
269 			piter = NULL;
270 			while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
271 				found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
272 				if (found) {
273 					ret = ucl_schema_validate (prop, found, true, err, root);
274 				}
275 			}
276 		}
277 		else if (elt->type == UCL_OBJECT &&
278 				strcmp (ucl_object_key (elt), "dependencies") == 0) {
279 			ret = ucl_schema_validate_dependencies (elt, obj, err, root);
280 		}
281 	}
282 
283 	if (ret) {
284 		/* Additional properties */
285 		if (!allow_additional || additional_schema != NULL) {
286 			/* Check if we have exactly the same properties in schema and object */
287 			iter = NULL;
288 			prop = ucl_object_find_key (schema, "properties");
289 			while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
290 				found = ucl_object_find_key (prop, ucl_object_key (elt));
291 				if (found == NULL) {
292 					/* Try patternProperties */
293 					piter = NULL;
294 					pat = ucl_object_find_key (schema, "patternProperties");
295 					while ((pelt = ucl_iterate_object (pat, &piter, true)) != NULL) {
296 						found = ucl_schema_test_pattern (obj, ucl_object_key (pelt));
297 						if (found != NULL) {
298 							break;
299 						}
300 					}
301 				}
302 				if (found == NULL) {
303 					if (!allow_additional) {
304 						ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
305 								"object has non-allowed property %s",
306 								ucl_object_key (elt));
307 						ret = false;
308 						break;
309 					}
310 					else if (additional_schema != NULL) {
311 						if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
312 							ret = false;
313 							break;
314 						}
315 					}
316 				}
317 			}
318 		}
319 		/* Required properties */
320 		if (required != NULL) {
321 			iter = NULL;
322 			while ((elt = ucl_iterate_object (required, &iter, true)) != NULL) {
323 				if (ucl_object_find_key (obj, ucl_object_tostring (elt)) == NULL) {
324 					ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
325 							"object has missing property %s",
326 							ucl_object_tostring (elt));
327 					ret = false;
328 					break;
329 				}
330 			}
331 		}
332 	}
333 
334 
335 	return ret;
336 }
337 
338 static bool
339 ucl_schema_validate_number (const ucl_object_t *schema,
340 		const ucl_object_t *obj, struct ucl_schema_error *err)
341 {
342 	const ucl_object_t *elt, *test;
343 	ucl_object_iter_t iter = NULL;
344 	bool ret = true, exclusive = false;
345 	double constraint, val;
346 	const double alpha = 1e-16;
347 
348 	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
349 		if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
350 				strcmp (ucl_object_key (elt), "multipleOf") == 0) {
351 			constraint = ucl_object_todouble (elt);
352 			if (constraint <= 0) {
353 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
354 						"multipleOf must be greater than zero");
355 				ret = false;
356 				break;
357 			}
358 			val = ucl_object_todouble (obj);
359 			if (fabs (remainder (val, constraint)) > alpha) {
360 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
361 						"number %.4f is not multiple of %.4f, remainder is %.7f",
362 						val, constraint);
363 				ret = false;
364 				break;
365 			}
366 		}
367 		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
368 			strcmp (ucl_object_key (elt), "maximum") == 0) {
369 			constraint = ucl_object_todouble (elt);
370 			test = ucl_object_find_key (schema, "exclusiveMaximum");
371 			if (test && test->type == UCL_BOOLEAN) {
372 				exclusive = ucl_object_toboolean (test);
373 			}
374 			val = ucl_object_todouble (obj);
375 			if (val > constraint || (exclusive && val >= constraint)) {
376 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
377 						"number is too big: %.3f, maximum is: %.3f",
378 						val, constraint);
379 				ret = false;
380 				break;
381 			}
382 		}
383 		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
384 				strcmp (ucl_object_key (elt), "minimum") == 0) {
385 			constraint = ucl_object_todouble (elt);
386 			test = ucl_object_find_key (schema, "exclusiveMinimum");
387 			if (test && test->type == UCL_BOOLEAN) {
388 				exclusive = ucl_object_toboolean (test);
389 			}
390 			val = ucl_object_todouble (obj);
391 			if (val < constraint || (exclusive && val <= constraint)) {
392 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
393 						"number is too small: %.3f, minimum is: %.3f",
394 						val, constraint);
395 				ret = false;
396 				break;
397 			}
398 		}
399 	}
400 
401 	return ret;
402 }
403 
404 static bool
405 ucl_schema_validate_string (const ucl_object_t *schema,
406 		const ucl_object_t *obj, struct ucl_schema_error *err)
407 {
408 	const ucl_object_t *elt;
409 	ucl_object_iter_t iter = NULL;
410 	bool ret = true;
411 	int64_t constraint;
412 #ifdef HAVE_REGEX_H
413 	regex_t re;
414 #endif
415 
416 	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
417 		if (elt->type == UCL_INT &&
418 			strcmp (ucl_object_key (elt), "maxLength") == 0) {
419 			constraint = ucl_object_toint (elt);
420 			if (obj->len > constraint) {
421 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
422 						"string is too big: %.3f, maximum is: %.3f",
423 						obj->len, constraint);
424 				ret = false;
425 				break;
426 			}
427 		}
428 		else if (elt->type == UCL_INT &&
429 				strcmp (ucl_object_key (elt), "minLength") == 0) {
430 			constraint = ucl_object_toint (elt);
431 			if (obj->len < constraint) {
432 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
433 						"string is too short: %.3f, minimum is: %.3f",
434 						obj->len, constraint);
435 				ret = false;
436 				break;
437 			}
438 		}
439 #ifdef HAVE_REGEX_H
440 		else if (elt->type == UCL_STRING &&
441 				strcmp (ucl_object_key (elt), "pattern") == 0) {
442 			if (regcomp (&re, ucl_object_tostring (elt),
443 					REG_EXTENDED | REG_NOSUB) != 0) {
444 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
445 						"cannot compile pattern %s", ucl_object_tostring (elt));
446 				ret = false;
447 				break;
448 			}
449 			if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
450 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
451 						"string doesn't match regexp %s",
452 						ucl_object_tostring (elt));
453 				ret = false;
454 			}
455 			regfree (&re);
456 		}
457 #endif
458 	}
459 
460 	return ret;
461 }
462 
463 struct ucl_compare_node {
464 	const ucl_object_t *obj;
465 	TREE_ENTRY(ucl_compare_node) link;
466 	struct ucl_compare_node *next;
467 };
468 
469 typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
470 
471 TREE_DEFINE(ucl_compare_node, link)
472 
473 static int
474 ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
475 {
476 	const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
477 
478 	return ucl_object_compare (o1, o2);
479 }
480 
481 static bool
482 ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
483 {
484 	ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
485 	ucl_object_iter_t iter = NULL;
486 	const ucl_object_t *elt;
487 	struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
488 	bool ret = true;
489 
490 	while ((elt = ucl_iterate_object (obj, &iter, true)) != NULL) {
491 		test.obj = elt;
492 		node = TREE_FIND (&tree, ucl_compare_node, link, &test);
493 		if (node != NULL) {
494 			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
495 					"duplicate values detected while uniqueItems is true");
496 			ret = false;
497 			break;
498 		}
499 		node = calloc (1, sizeof (*node));
500 		if (node == NULL) {
501 			ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
502 					"cannot allocate tree node");
503 			ret = false;
504 			break;
505 		}
506 		node->obj = elt;
507 		TREE_INSERT (&tree, ucl_compare_node, link, node);
508 		LL_PREPEND (nodes, node);
509 	}
510 
511 	LL_FOREACH_SAFE (nodes, node, tmp) {
512 		free (node);
513 	}
514 
515 	return ret;
516 }
517 
518 static bool
519 ucl_schema_validate_array (const ucl_object_t *schema,
520 		const ucl_object_t *obj, struct ucl_schema_error *err,
521 		const ucl_object_t *root)
522 {
523 	const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
524 			*first_unvalidated = NULL;
525 	ucl_object_iter_t iter = NULL, piter = NULL;
526 	bool ret = true, allow_additional = true, need_unique = false;
527 	int64_t minmax;
528 	unsigned int idx = 0;
529 
530 	while (ret && (elt = ucl_iterate_object (schema, &iter, true)) != NULL) {
531 		if (strcmp (ucl_object_key (elt), "items") == 0) {
532 			if (elt->type == UCL_ARRAY) {
533 				found = ucl_array_head (obj);
534 				while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
535 					if (found) {
536 						ret = ucl_schema_validate (it, found, false, err, root);
537 						found = ucl_array_find_index (obj, ++idx);
538 					}
539 				}
540 				if (found != NULL) {
541 					/* The first element that is not validated */
542 					first_unvalidated = found;
543 				}
544 			}
545 			else if (elt->type == UCL_OBJECT) {
546 				/* Validate all items using the specified schema */
547 				while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
548 					ret = ucl_schema_validate (elt, it, false, err, root);
549 				}
550 			}
551 			else {
552 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
553 						"items attribute is invalid in schema");
554 				ret = false;
555 				break;
556 			}
557 		}
558 		else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
559 			if (elt->type == UCL_BOOLEAN) {
560 				if (!ucl_object_toboolean (elt)) {
561 					/* Deny additional fields completely */
562 					allow_additional = false;
563 				}
564 			}
565 			else if (elt->type == UCL_OBJECT) {
566 				/* Define validator for additional fields */
567 				additional_schema = elt;
568 			}
569 			else {
570 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
571 						"additionalItems attribute is invalid in schema");
572 				ret = false;
573 				break;
574 			}
575 		}
576 		else if (elt->type == UCL_BOOLEAN &&
577 				strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
578 			need_unique = ucl_object_toboolean (elt);
579 		}
580 		else if (strcmp (ucl_object_key (elt), "minItems") == 0
581 				&& ucl_object_toint_safe (elt, &minmax)) {
582 			if (obj->len < minmax) {
583 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
584 						"array has not enough items: %u, minimum is: %u",
585 						obj->len, (unsigned)minmax);
586 				ret = false;
587 				break;
588 			}
589 		}
590 		else if (strcmp (ucl_object_key (elt), "maxItems") == 0
591 				&& ucl_object_toint_safe (elt, &minmax)) {
592 			if (obj->len > minmax) {
593 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
594 						"array has too many items: %u, maximum is: %u",
595 						obj->len, (unsigned)minmax);
596 				ret = false;
597 				break;
598 			}
599 		}
600 	}
601 
602 	if (ret) {
603 		/* Additional properties */
604 		if (!allow_additional || additional_schema != NULL) {
605 			if (first_unvalidated != NULL) {
606 				if (!allow_additional) {
607 					ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
608 							"array has undefined item");
609 					ret = false;
610 				}
611 				else if (additional_schema != NULL) {
612 					elt = ucl_array_find_index (obj, idx);
613 					while (elt) {
614 						if (!ucl_schema_validate (additional_schema, elt, false,
615 								err, root)) {
616 							ret = false;
617 							break;
618 						}
619 						elt = ucl_array_find_index (obj, idx ++);
620 					}
621 				}
622 			}
623 		}
624 		/* Required properties */
625 		if (ret && need_unique) {
626 			ret = ucl_schema_array_is_unique (obj, err);
627 		}
628 	}
629 
630 	return ret;
631 }
632 
633 /*
634  * Returns whether this object is allowed for this type
635  */
636 static bool
637 ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
638 		struct ucl_schema_error *err)
639 {
640 	ucl_object_iter_t iter = NULL;
641 	const ucl_object_t *elt;
642 	const char *type_str;
643 	ucl_type_t t;
644 
645 	if (type == NULL) {
646 		/* Any type is allowed */
647 		return true;
648 	}
649 
650 	if (type->type == UCL_ARRAY) {
651 		/* One of allowed types */
652 		while ((elt = ucl_iterate_object (type, &iter, true)) != NULL) {
653 			if (ucl_schema_type_is_allowed (elt, obj, err)) {
654 				return true;
655 			}
656 		}
657 	}
658 	else if (type->type == UCL_STRING) {
659 		type_str = ucl_object_tostring (type);
660 		if (!ucl_string_to_type (type_str, &t)) {
661 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
662 					"Type attribute is invalid in schema");
663 			return false;
664 		}
665 		if (obj->type != t) {
666 			/* Some types are actually compatible */
667 			if (obj->type == UCL_TIME && t == UCL_FLOAT) {
668 				return true;
669 			}
670 			else if (obj->type == UCL_INT && t == UCL_FLOAT) {
671 				return true;
672 			}
673 			else {
674 				ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
675 						"Invalid type of %s, expected %s",
676 						ucl_object_type_to_string (obj->type),
677 						ucl_object_type_to_string (t));
678 			}
679 		}
680 		else {
681 			/* Types are equal */
682 			return true;
683 		}
684 	}
685 
686 	return false;
687 }
688 
689 /*
690  * Check if object is equal to one of elements of enum
691  */
692 static bool
693 ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
694 		struct ucl_schema_error *err)
695 {
696 	ucl_object_iter_t iter = NULL;
697 	const ucl_object_t *elt;
698 	bool ret = false;
699 
700 	while ((elt = ucl_iterate_object (en, &iter, true)) != NULL) {
701 		if (ucl_object_compare (elt, obj) == 0) {
702 			ret = true;
703 			break;
704 		}
705 	}
706 
707 	if (!ret) {
708 		ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
709 				"object is not one of enumerated patterns");
710 	}
711 
712 	return ret;
713 }
714 
715 
716 /*
717  * Check a single ref component
718  */
719 static const ucl_object_t *
720 ucl_schema_resolve_ref_component (const ucl_object_t *cur,
721 		const char *refc, int len,
722 		struct ucl_schema_error *err)
723 {
724 	const ucl_object_t *res = NULL;
725 	char *err_str;
726 	int num, i;
727 
728 	if (cur->type == UCL_OBJECT) {
729 		/* Find a key inside an object */
730 		res = ucl_object_find_keyl (cur, refc, len);
731 		if (res == NULL) {
732 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
733 					"reference %s is invalid, missing path component", refc);
734 			return NULL;
735 		}
736 	}
737 	else if (cur->type == UCL_ARRAY) {
738 		/* We must figure out a number inside array */
739 		num = strtoul (refc, &err_str, 10);
740 		if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
741 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
742 					"reference %s is invalid, invalid item number", refc);
743 			return NULL;
744 		}
745 		res = ucl_array_head (cur);
746 		i = 0;
747 		while (res != NULL) {
748 			if (i == num) {
749 				break;
750 			}
751 			res = res->next;
752 		}
753 		if (res == NULL) {
754 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
755 					"reference %s is invalid, item number %d does not exist",
756 					refc, num);
757 			return NULL;
758 		}
759 	}
760 	else {
761 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
762 				"reference %s is invalid, contains primitive object in the path",
763 				refc);
764 		return NULL;
765 	}
766 
767 	return res;
768 }
769 /*
770  * Find reference schema
771  */
772 static const ucl_object_t *
773 ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
774 		struct ucl_schema_error *err)
775 {
776 	const char *p, *c;
777 	const ucl_object_t *res = NULL;
778 
779 
780 	if (ref[0] != '#') {
781 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
782 				"reference %s is invalid, not started with #", ref);
783 		return NULL;
784 	}
785 	if (ref[1] == '/') {
786 		p = &ref[2];
787 	}
788 	else if (ref[1] == '\0') {
789 		return root;
790 	}
791 	else {
792 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
793 				"reference %s is invalid, not started with #/", ref);
794 		return NULL;
795 	}
796 
797 	c = p;
798 	res = root;
799 
800 	while (*p != '\0') {
801 		if (*p == '/') {
802 			if (p - c == 0) {
803 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
804 						"reference %s is invalid, empty path component", ref);
805 				return NULL;
806 			}
807 			/* Now we have some url part, so we need to figure out where we are */
808 			res = ucl_schema_resolve_ref_component (res, c, p - c, err);
809 			if (res == NULL) {
810 				return NULL;
811 			}
812 			c = p + 1;
813 		}
814 		p ++;
815 	}
816 
817 	if (p - c != 0) {
818 		res = ucl_schema_resolve_ref_component (res, c, p - c, err);
819 	}
820 
821 	if (res == NULL || res->type != UCL_OBJECT) {
822 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
823 				"reference %s is invalid, cannot find specified object",
824 				ref);
825 		return NULL;
826 	}
827 
828 	return res;
829 }
830 
831 static bool
832 ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
833 		struct ucl_schema_error *err)
834 {
835 	const ucl_object_t *elt, *cur;
836 	int64_t constraint, i;
837 
838 	elt = ucl_object_find_key (schema, "maxValues");
839 	if (elt != NULL && elt->type == UCL_INT) {
840 		constraint = ucl_object_toint (elt);
841 		cur = obj;
842 		i = 0;
843 		while (cur) {
844 			if (i > constraint) {
845 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
846 					"object has more values than defined: %ld",
847 					(long int)constraint);
848 				return false;
849 			}
850 			i ++;
851 			cur = cur->next;
852 		}
853 	}
854 	elt = ucl_object_find_key (schema, "minValues");
855 	if (elt != NULL && elt->type == UCL_INT) {
856 		constraint = ucl_object_toint (elt);
857 		cur = obj;
858 		i = 0;
859 		while (cur) {
860 			if (i >= constraint) {
861 				break;
862 			}
863 			i ++;
864 			cur = cur->next;
865 		}
866 		if (i < constraint) {
867 			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
868 					"object has less values than defined: %ld",
869 					(long int)constraint);
870 			return false;
871 		}
872 	}
873 
874 	return true;
875 }
876 
877 static bool
878 ucl_schema_validate (const ucl_object_t *schema,
879 		const ucl_object_t *obj, bool try_array,
880 		struct ucl_schema_error *err,
881 		const ucl_object_t *root)
882 {
883 	const ucl_object_t *elt, *cur;
884 	ucl_object_iter_t iter = NULL;
885 	bool ret;
886 
887 	if (schema->type != UCL_OBJECT) {
888 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
889 				"schema is %s instead of object", ucl_object_type_to_string (schema->type));
890 		return false;
891 	}
892 
893 	if (try_array) {
894 		/*
895 		 * Special case for multiple values
896 		 */
897 		if (!ucl_schema_validate_values (schema, obj, err)) {
898 			return false;
899 		}
900 		LL_FOREACH (obj, cur) {
901 			if (!ucl_schema_validate (schema, cur, false, err, root)) {
902 				return false;
903 			}
904 		}
905 		return true;
906 	}
907 
908 	elt = ucl_object_find_key (schema, "enum");
909 	if (elt != NULL && elt->type == UCL_ARRAY) {
910 		if (!ucl_schema_validate_enum (elt, obj, err)) {
911 			return false;
912 		}
913 	}
914 
915 	elt = ucl_object_find_key (schema, "allOf");
916 	if (elt != NULL && elt->type == UCL_ARRAY) {
917 		iter = NULL;
918 		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
919 			ret = ucl_schema_validate (cur, obj, true, err, root);
920 			if (!ret) {
921 				return false;
922 			}
923 		}
924 	}
925 
926 	elt = ucl_object_find_key (schema, "anyOf");
927 	if (elt != NULL && elt->type == UCL_ARRAY) {
928 		iter = NULL;
929 		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
930 			ret = ucl_schema_validate (cur, obj, true, err, root);
931 			if (ret) {
932 				break;
933 			}
934 		}
935 		if (!ret) {
936 			return false;
937 		}
938 		else {
939 			/* Reset error */
940 			err->code = UCL_SCHEMA_OK;
941 		}
942 	}
943 
944 	elt = ucl_object_find_key (schema, "oneOf");
945 	if (elt != NULL && elt->type == UCL_ARRAY) {
946 		iter = NULL;
947 		ret = false;
948 		while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
949 			if (!ret) {
950 				ret = ucl_schema_validate (cur, obj, true, err, root);
951 			}
952 			else if (ucl_schema_validate (cur, obj, true, err, root)) {
953 				ret = false;
954 				break;
955 			}
956 		}
957 		if (!ret) {
958 			return false;
959 		}
960 	}
961 
962 	elt = ucl_object_find_key (schema, "not");
963 	if (elt != NULL && elt->type == UCL_OBJECT) {
964 		if (ucl_schema_validate (elt, obj, true, err, root)) {
965 			return false;
966 		}
967 		else {
968 			/* Reset error */
969 			err->code = UCL_SCHEMA_OK;
970 		}
971 	}
972 
973 	elt = ucl_object_find_key (schema, "$ref");
974 	if (elt != NULL) {
975 		cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
976 		if (cur == NULL) {
977 			return false;
978 		}
979 		if (!ucl_schema_validate (cur, obj, try_array, err, root)) {
980 			return false;
981 		}
982 	}
983 
984 	elt = ucl_object_find_key (schema, "type");
985 	if (!ucl_schema_type_is_allowed (elt, obj, err)) {
986 		return false;
987 	}
988 
989 	switch (obj->type) {
990 	case UCL_OBJECT:
991 		return ucl_schema_validate_object (schema, obj, err, root);
992 		break;
993 	case UCL_ARRAY:
994 		return ucl_schema_validate_array (schema, obj, err, root);
995 		break;
996 	case UCL_INT:
997 	case UCL_FLOAT:
998 		return ucl_schema_validate_number (schema, obj, err);
999 		break;
1000 	case UCL_STRING:
1001 		return ucl_schema_validate_string (schema, obj, err);
1002 		break;
1003 	default:
1004 		break;
1005 	}
1006 
1007 	return true;
1008 }
1009 
1010 bool
1011 ucl_object_validate (const ucl_object_t *schema,
1012 		const ucl_object_t *obj, struct ucl_schema_error *err)
1013 {
1014 	return ucl_schema_validate (schema, obj, true, err, schema);
1015 }
1016