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