2015-02-21 19:11:12 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, Vsevolod Stakhov
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ucl.h"
|
|
|
|
#include "ucl_internal.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include "utlist.h"
|
|
|
|
#ifdef HAVE_STDARG_H
|
|
|
|
#include <stdarg.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STDIO_H
|
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
#include <regex.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_MATH_H
|
|
|
|
#include <math.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static bool ucl_schema_validate (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, bool try_array,
|
|
|
|
struct ucl_schema_error *err,
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create validation error
|
|
|
|
*/
|
2018-09-30 19:36:51 +02:00
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
static inline void
|
|
|
|
ucl_schema_create_error (struct ucl_schema_error *err,
|
|
|
|
enum ucl_schema_error_code code, const ucl_object_t *obj,
|
|
|
|
const char *fmt, ...)
|
|
|
|
__attribute__ (( format( printf, 4, 5) ));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline void
|
2015-02-21 19:11:12 +01:00
|
|
|
ucl_schema_create_error (struct ucl_schema_error *err,
|
|
|
|
enum ucl_schema_error_code code, const ucl_object_t *obj,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
if (err != NULL) {
|
|
|
|
err->code = code;
|
|
|
|
err->obj = obj;
|
|
|
|
va_start (va, fmt);
|
|
|
|
vsnprintf (err->msg, sizeof (err->msg), fmt, va);
|
|
|
|
va_end (va);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether we have a pattern specified
|
|
|
|
*/
|
|
|
|
static const ucl_object_t *
|
2018-09-30 19:36:51 +02:00
|
|
|
ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern, bool recursive)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
|
|
|
const ucl_object_t *res = NULL;
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
regex_t reg;
|
|
|
|
const ucl_object_t *elt;
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
|
|
|
|
if (regcomp (®, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
|
2018-09-30 19:36:51 +02:00
|
|
|
if (recursive) {
|
|
|
|
while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
|
|
|
|
if (regexec (®, ucl_object_key (elt), 0, NULL, 0) == 0) {
|
|
|
|
res = elt;
|
|
|
|
break;
|
|
|
|
}
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
2018-09-30 19:36:51 +02:00
|
|
|
} else {
|
|
|
|
if (regexec (®, ucl_object_key (obj), 0, NULL, 0) == 0)
|
|
|
|
res = obj;
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
regfree (®);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check dependencies for an object
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_dependencies (const ucl_object_t *deps,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err,
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *ext_ref)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
|
|
|
const ucl_object_t *elt, *cur, *cur_dep;
|
|
|
|
ucl_object_iter_t iter = NULL, piter;
|
|
|
|
bool ret = true;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (cur = ucl_object_iterate (deps, &iter, true)) != NULL) {
|
|
|
|
elt = ucl_object_lookup (obj, ucl_object_key (cur));
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL) {
|
|
|
|
/* Need to check dependencies */
|
|
|
|
if (cur->type == UCL_ARRAY) {
|
|
|
|
piter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (cur_dep = ucl_object_iterate (cur, &piter, true)) != NULL) {
|
|
|
|
if (ucl_object_lookup (obj, ucl_object_tostring (cur_dep)) == NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
|
|
|
|
"dependency %s is missing for key %s",
|
|
|
|
ucl_object_tostring (cur_dep), ucl_object_key (cur));
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cur->type == UCL_OBJECT) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (cur, obj, true, err, root, ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate object
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_object (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err,
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *ext_ref)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
|
|
|
const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
|
|
|
|
*required = NULL, *pat, *pelt;
|
|
|
|
ucl_object_iter_t iter = NULL, piter = NULL;
|
|
|
|
bool ret = true, allow_additional = true;
|
|
|
|
int64_t minmax;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt->type == UCL_OBJECT &&
|
|
|
|
strcmp (ucl_object_key (elt), "properties") == 0) {
|
|
|
|
piter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
|
|
|
|
found = ucl_object_lookup (obj, ucl_object_key (prop));
|
2015-02-21 19:11:12 +01:00
|
|
|
if (found) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (prop, found, true, err, root,
|
|
|
|
ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
|
|
|
|
if (elt->type == UCL_BOOLEAN) {
|
|
|
|
if (!ucl_object_toboolean (elt)) {
|
|
|
|
/* Deny additional fields completely */
|
|
|
|
allow_additional = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_OBJECT) {
|
|
|
|
/* Define validator for additional fields */
|
|
|
|
additional_schema = elt;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"additionalProperties attribute is invalid in schema");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "required") == 0) {
|
|
|
|
if (elt->type == UCL_ARRAY) {
|
|
|
|
required = elt;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"required attribute is invalid in schema");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "minProperties") == 0
|
|
|
|
&& ucl_object_toint_safe (elt, &minmax)) {
|
|
|
|
if (obj->len < minmax) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object has not enough properties: %u, minimum is: %u",
|
|
|
|
obj->len, (unsigned)minmax);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
|
|
|
|
&& ucl_object_toint_safe (elt, &minmax)) {
|
|
|
|
if (obj->len > minmax) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object has too many properties: %u, maximum is: %u",
|
|
|
|
obj->len, (unsigned)minmax);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
|
2018-09-30 19:36:51 +02:00
|
|
|
const ucl_object_t *vobj;
|
|
|
|
ucl_object_iter_t viter;
|
2015-02-21 19:11:12 +01:00
|
|
|
piter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
|
2018-09-30 19:36:51 +02:00
|
|
|
viter = NULL;
|
|
|
|
while (ret && (vobj = ucl_object_iterate (obj, &viter, true)) != NULL) {
|
|
|
|
found = ucl_schema_test_pattern (vobj, ucl_object_key (prop), false);
|
|
|
|
if (found) {
|
|
|
|
ret = ucl_schema_validate (prop, found, true, err, root,
|
|
|
|
ext_ref);
|
|
|
|
}
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_OBJECT &&
|
|
|
|
strcmp (ucl_object_key (elt), "dependencies") == 0) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate_dependencies (elt, obj, err, root,
|
|
|
|
ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
/* Additional properties */
|
|
|
|
if (!allow_additional || additional_schema != NULL) {
|
|
|
|
/* Check if we have exactly the same properties in schema and object */
|
|
|
|
iter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
prop = ucl_object_lookup (schema, "properties");
|
|
|
|
while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
|
|
|
|
found = ucl_object_lookup (prop, ucl_object_key (elt));
|
2015-02-21 19:11:12 +01:00
|
|
|
if (found == NULL) {
|
|
|
|
/* Try patternProperties */
|
|
|
|
piter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
pat = ucl_object_lookup (schema, "patternProperties");
|
|
|
|
while ((pelt = ucl_object_iterate (pat, &piter, true)) != NULL) {
|
2018-09-30 19:36:51 +02:00
|
|
|
found = ucl_schema_test_pattern (obj, ucl_object_key (pelt), true);
|
2015-02-21 19:11:12 +01:00
|
|
|
if (found != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found == NULL) {
|
|
|
|
if (!allow_additional) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object has non-allowed property %s",
|
|
|
|
ucl_object_key (elt));
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (additional_schema != NULL) {
|
2015-12-27 16:12:28 +01:00
|
|
|
if (!ucl_schema_validate (additional_schema, elt,
|
|
|
|
true, err, root, ext_ref)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Required properties */
|
|
|
|
if (required != NULL) {
|
|
|
|
iter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((elt = ucl_object_iterate (required, &iter, true)) != NULL) {
|
|
|
|
if (ucl_object_lookup (obj, ucl_object_tostring (elt)) == NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
|
|
|
|
"object has missing property %s",
|
|
|
|
ucl_object_tostring (elt));
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_number (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
const ucl_object_t *elt, *test;
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
bool ret = true, exclusive = false;
|
|
|
|
double constraint, val;
|
|
|
|
const double alpha = 1e-16;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
|
|
strcmp (ucl_object_key (elt), "multipleOf") == 0) {
|
|
|
|
constraint = ucl_object_todouble (elt);
|
|
|
|
if (constraint <= 0) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"multipleOf must be greater than zero");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
val = ucl_object_todouble (obj);
|
|
|
|
if (fabs (remainder (val, constraint)) > alpha) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"number %.4f is not multiple of %.4f, remainder is %.7f",
|
2018-09-30 19:36:51 +02:00
|
|
|
val, constraint, remainder (val, constraint));
|
2015-02-21 19:11:12 +01:00
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
|
|
strcmp (ucl_object_key (elt), "maximum") == 0) {
|
|
|
|
constraint = ucl_object_todouble (elt);
|
2016-02-16 10:59:52 +01:00
|
|
|
test = ucl_object_lookup (schema, "exclusiveMaximum");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (test && test->type == UCL_BOOLEAN) {
|
|
|
|
exclusive = ucl_object_toboolean (test);
|
|
|
|
}
|
|
|
|
val = ucl_object_todouble (obj);
|
|
|
|
if (val > constraint || (exclusive && val >= constraint)) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"number is too big: %.3f, maximum is: %.3f",
|
|
|
|
val, constraint);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
|
|
strcmp (ucl_object_key (elt), "minimum") == 0) {
|
|
|
|
constraint = ucl_object_todouble (elt);
|
2016-02-16 10:59:52 +01:00
|
|
|
test = ucl_object_lookup (schema, "exclusiveMinimum");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (test && test->type == UCL_BOOLEAN) {
|
|
|
|
exclusive = ucl_object_toboolean (test);
|
|
|
|
}
|
|
|
|
val = ucl_object_todouble (obj);
|
|
|
|
if (val < constraint || (exclusive && val <= constraint)) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"number is too small: %.3f, minimum is: %.3f",
|
|
|
|
val, constraint);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_string (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
const ucl_object_t *elt;
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
bool ret = true;
|
|
|
|
int64_t constraint;
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
regex_t re;
|
|
|
|
#endif
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt->type == UCL_INT &&
|
|
|
|
strcmp (ucl_object_key (elt), "maxLength") == 0) {
|
|
|
|
constraint = ucl_object_toint (elt);
|
|
|
|
if (obj->len > constraint) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
2018-09-30 19:36:51 +02:00
|
|
|
"string is too big: %u, maximum is: %" PRId64,
|
2015-02-21 19:11:12 +01:00
|
|
|
obj->len, constraint);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_INT &&
|
|
|
|
strcmp (ucl_object_key (elt), "minLength") == 0) {
|
|
|
|
constraint = ucl_object_toint (elt);
|
|
|
|
if (obj->len < constraint) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
2018-09-30 19:36:51 +02:00
|
|
|
"string is too short: %u, minimum is: %" PRId64,
|
2015-02-21 19:11:12 +01:00
|
|
|
obj->len, constraint);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
else if (elt->type == UCL_STRING &&
|
|
|
|
strcmp (ucl_object_key (elt), "pattern") == 0) {
|
|
|
|
if (regcomp (&re, ucl_object_tostring (elt),
|
|
|
|
REG_EXTENDED | REG_NOSUB) != 0) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"cannot compile pattern %s", ucl_object_tostring (elt));
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"string doesn't match regexp %s",
|
|
|
|
ucl_object_tostring (elt));
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
regfree (&re);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ucl_compare_node {
|
|
|
|
const ucl_object_t *obj;
|
|
|
|
TREE_ENTRY(ucl_compare_node) link;
|
|
|
|
struct ucl_compare_node *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
|
|
|
|
|
|
|
|
TREE_DEFINE(ucl_compare_node, link)
|
|
|
|
|
|
|
|
static int
|
|
|
|
ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
|
|
|
|
{
|
|
|
|
const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
|
|
|
|
|
|
|
|
return ucl_object_compare (o1, o2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
const ucl_object_t *elt;
|
|
|
|
struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
|
|
|
|
bool ret = true;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
test.obj = elt;
|
|
|
|
node = TREE_FIND (&tree, ucl_compare_node, link, &test);
|
|
|
|
if (node != NULL) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
|
|
|
|
"duplicate values detected while uniqueItems is true");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = calloc (1, sizeof (*node));
|
|
|
|
if (node == NULL) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
|
|
|
|
"cannot allocate tree node");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node->obj = elt;
|
|
|
|
TREE_INSERT (&tree, ucl_compare_node, link, node);
|
|
|
|
LL_PREPEND (nodes, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
LL_FOREACH_SAFE (nodes, node, tmp) {
|
|
|
|
free (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_array (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err,
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *ext_ref)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
|
|
|
const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
|
|
|
|
*first_unvalidated = NULL;
|
|
|
|
ucl_object_iter_t iter = NULL, piter = NULL;
|
|
|
|
bool ret = true, allow_additional = true, need_unique = false;
|
|
|
|
int64_t minmax;
|
|
|
|
unsigned int idx = 0;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (strcmp (ucl_object_key (elt), "items") == 0) {
|
|
|
|
if (elt->type == UCL_ARRAY) {
|
|
|
|
found = ucl_array_head (obj);
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (it = ucl_object_iterate (elt, &piter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (found) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (it, found, false, err,
|
|
|
|
root, ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
found = ucl_array_find_index (obj, ++idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found != NULL) {
|
|
|
|
/* The first element that is not validated */
|
|
|
|
first_unvalidated = found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_OBJECT) {
|
|
|
|
/* Validate all items using the specified schema */
|
2016-02-16 10:59:52 +01:00
|
|
|
while (ret && (it = ucl_object_iterate (obj, &piter, true)) != NULL) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (elt, it, false, err, root,
|
|
|
|
ext_ref);
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"items attribute is invalid in schema");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
|
|
|
|
if (elt->type == UCL_BOOLEAN) {
|
|
|
|
if (!ucl_object_toboolean (elt)) {
|
|
|
|
/* Deny additional fields completely */
|
|
|
|
allow_additional = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_OBJECT) {
|
|
|
|
/* Define validator for additional fields */
|
|
|
|
additional_schema = elt;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
|
|
"additionalItems attribute is invalid in schema");
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (elt->type == UCL_BOOLEAN &&
|
|
|
|
strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
|
|
|
|
need_unique = ucl_object_toboolean (elt);
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "minItems") == 0
|
|
|
|
&& ucl_object_toint_safe (elt, &minmax)) {
|
|
|
|
if (obj->len < minmax) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"array has not enough items: %u, minimum is: %u",
|
|
|
|
obj->len, (unsigned)minmax);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (ucl_object_key (elt), "maxItems") == 0
|
|
|
|
&& ucl_object_toint_safe (elt, &minmax)) {
|
|
|
|
if (obj->len > minmax) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"array has too many items: %u, maximum is: %u",
|
|
|
|
obj->len, (unsigned)minmax);
|
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
/* Additional properties */
|
|
|
|
if (!allow_additional || additional_schema != NULL) {
|
|
|
|
if (first_unvalidated != NULL) {
|
|
|
|
if (!allow_additional) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"array has undefined item");
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
else if (additional_schema != NULL) {
|
|
|
|
elt = ucl_array_find_index (obj, idx);
|
|
|
|
while (elt) {
|
|
|
|
if (!ucl_schema_validate (additional_schema, elt, false,
|
2015-12-27 16:12:28 +01:00
|
|
|
err, root, ext_ref)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
elt = ucl_array_find_index (obj, idx ++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Required properties */
|
|
|
|
if (ret && need_unique) {
|
|
|
|
ret = ucl_schema_array_is_unique (obj, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns whether this object is allowed for this type
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
const ucl_object_t *elt;
|
|
|
|
const char *type_str;
|
|
|
|
ucl_type_t t;
|
|
|
|
|
|
|
|
if (type == NULL) {
|
|
|
|
/* Any type is allowed */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type->type == UCL_ARRAY) {
|
|
|
|
/* One of allowed types */
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((elt = ucl_object_iterate (type, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (ucl_schema_type_is_allowed (elt, obj, err)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type->type == UCL_STRING) {
|
|
|
|
type_str = ucl_object_tostring (type);
|
2015-12-27 16:12:28 +01:00
|
|
|
if (!ucl_object_string_to_type (type_str, &t)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
|
|
|
|
"Type attribute is invalid in schema");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (obj->type != t) {
|
|
|
|
/* Some types are actually compatible */
|
|
|
|
if (obj->type == UCL_TIME && t == UCL_FLOAT) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (obj->type == UCL_INT && t == UCL_FLOAT) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
|
|
|
|
"Invalid type of %s, expected %s",
|
|
|
|
ucl_object_type_to_string (obj->type),
|
|
|
|
ucl_object_type_to_string (t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Types are equal */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if object is equal to one of elements of enum
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
const ucl_object_t *elt;
|
|
|
|
bool ret = false;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((elt = ucl_object_iterate (en, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (ucl_object_compare (elt, obj) == 0) {
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object is not one of enumerated patterns");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check a single ref component
|
|
|
|
*/
|
|
|
|
static const ucl_object_t *
|
|
|
|
ucl_schema_resolve_ref_component (const ucl_object_t *cur,
|
|
|
|
const char *refc, int len,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
const ucl_object_t *res = NULL;
|
|
|
|
char *err_str;
|
|
|
|
int num, i;
|
|
|
|
|
|
|
|
if (cur->type == UCL_OBJECT) {
|
|
|
|
/* Find a key inside an object */
|
2016-02-16 10:59:52 +01:00
|
|
|
res = ucl_object_lookup_len (cur, refc, len);
|
2015-02-21 19:11:12 +01:00
|
|
|
if (res == NULL) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
|
|
"reference %s is invalid, missing path component", refc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cur->type == UCL_ARRAY) {
|
|
|
|
/* We must figure out a number inside array */
|
|
|
|
num = strtoul (refc, &err_str, 10);
|
|
|
|
if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
|
|
"reference %s is invalid, invalid item number", refc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
res = ucl_array_head (cur);
|
|
|
|
i = 0;
|
|
|
|
while (res != NULL) {
|
|
|
|
if (i == num) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
res = res->next;
|
|
|
|
}
|
|
|
|
if (res == NULL) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
|
|
"reference %s is invalid, item number %d does not exist",
|
|
|
|
refc, num);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
|
|
"reference %s is invalid, contains primitive object in the path",
|
|
|
|
refc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Find reference schema
|
|
|
|
*/
|
|
|
|
static const ucl_object_t *
|
|
|
|
ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
|
2015-12-27 16:12:28 +01:00
|
|
|
struct ucl_schema_error *err, ucl_object_t *ext_ref,
|
|
|
|
ucl_object_t const ** nroot)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
2015-12-27 16:12:28 +01:00
|
|
|
UT_string *url_err = NULL;
|
|
|
|
struct ucl_parser *parser;
|
|
|
|
const ucl_object_t *res = NULL, *ext_obj = NULL;
|
|
|
|
ucl_object_t *url_obj;
|
|
|
|
const char *p, *c, *hash_ptr = NULL;
|
|
|
|
char *url_copy = NULL;
|
|
|
|
unsigned char *url_buf;
|
|
|
|
size_t url_buflen;
|
2015-02-21 19:11:12 +01:00
|
|
|
|
|
|
|
if (ref[0] != '#') {
|
2015-12-27 16:12:28 +01:00
|
|
|
hash_ptr = strrchr (ref, '#');
|
|
|
|
|
|
|
|
if (hash_ptr) {
|
|
|
|
url_copy = malloc (hash_ptr - ref + 1);
|
|
|
|
|
|
|
|
if (url_copy == NULL) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INTERNAL_ERROR, root,
|
|
|
|
"cannot allocate memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ucl_strlcpy (url_copy, ref, hash_ptr - ref + 1);
|
|
|
|
p = url_copy;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Full URL */
|
|
|
|
p = ref;
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
ext_obj = ucl_object_lookup (ext_ref, p);
|
2015-12-27 16:12:28 +01:00
|
|
|
|
|
|
|
if (ext_obj == NULL) {
|
|
|
|
if (ucl_strnstr (p, "://", strlen (p)) != NULL) {
|
|
|
|
if (!ucl_fetch_url (p, &url_buf, &url_buflen, &url_err, true)) {
|
2016-02-08 19:26:58 +01:00
|
|
|
|
2015-12-27 16:12:28 +01:00
|
|
|
ucl_schema_create_error (err,
|
|
|
|
UCL_SCHEMA_INVALID_SCHEMA,
|
|
|
|
root,
|
|
|
|
"cannot fetch reference %s: %s",
|
|
|
|
p,
|
|
|
|
url_err != NULL ? utstring_body (url_err)
|
|
|
|
: "unknown");
|
2016-02-08 19:26:58 +01:00
|
|
|
free (url_copy);
|
|
|
|
|
2015-12-27 16:12:28 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err,
|
|
|
|
true)) {
|
|
|
|
ucl_schema_create_error (err,
|
|
|
|
UCL_SCHEMA_INVALID_SCHEMA,
|
|
|
|
root,
|
|
|
|
"cannot fetch reference %s: %s",
|
|
|
|
p,
|
|
|
|
url_err != NULL ? utstring_body (url_err)
|
|
|
|
: "unknown");
|
2016-02-08 19:26:58 +01:00
|
|
|
free (url_copy);
|
|
|
|
|
2015-12-27 16:12:28 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parser = ucl_parser_new (0);
|
|
|
|
|
|
|
|
if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
|
|
|
|
"cannot fetch reference %s: %s", p,
|
|
|
|
ucl_parser_get_error (parser));
|
|
|
|
ucl_parser_free (parser);
|
2016-02-08 19:26:58 +01:00
|
|
|
free (url_copy);
|
|
|
|
|
2015-12-27 16:12:28 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
url_obj = ucl_parser_get_object (parser);
|
|
|
|
ext_obj = url_obj;
|
|
|
|
ucl_object_insert_key (ext_ref, url_obj, p, 0, true);
|
|
|
|
free (url_buf);
|
|
|
|
}
|
|
|
|
|
2016-02-08 19:26:58 +01:00
|
|
|
free (url_copy);
|
|
|
|
|
2015-12-27 16:12:28 +01:00
|
|
|
if (hash_ptr) {
|
|
|
|
p = hash_ptr + 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p = "";
|
|
|
|
}
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
2015-12-27 16:12:28 +01:00
|
|
|
else {
|
|
|
|
p = ref + 1;
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
2015-12-27 16:12:28 +01:00
|
|
|
|
|
|
|
res = ext_obj != NULL ? ext_obj : root;
|
|
|
|
*nroot = res;
|
|
|
|
|
|
|
|
if (*p == '/') {
|
|
|
|
p++;
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
2015-12-27 16:12:28 +01:00
|
|
|
else if (*p == '\0') {
|
|
|
|
return res;
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
c = p;
|
|
|
|
|
|
|
|
while (*p != '\0') {
|
|
|
|
if (*p == '/') {
|
|
|
|
if (p - c == 0) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
|
|
"reference %s is invalid, empty path component", ref);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Now we have some url part, so we need to figure out where we are */
|
|
|
|
res = ucl_schema_resolve_ref_component (res, c, p - c, err);
|
|
|
|
if (res == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
c = p + 1;
|
|
|
|
}
|
|
|
|
p ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p - c != 0) {
|
|
|
|
res = ucl_schema_resolve_ref_component (res, c, p - c, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res == NULL || res->type != UCL_OBJECT) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
|
|
"reference %s is invalid, cannot find specified object",
|
|
|
|
ref);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
const ucl_object_t *elt, *cur;
|
|
|
|
int64_t constraint, i;
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "maxValues");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_INT) {
|
|
|
|
constraint = ucl_object_toint (elt);
|
|
|
|
cur = obj;
|
|
|
|
i = 0;
|
|
|
|
while (cur) {
|
|
|
|
if (i > constraint) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object has more values than defined: %ld",
|
|
|
|
(long int)constraint);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
i ++;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
}
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "minValues");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_INT) {
|
|
|
|
constraint = ucl_object_toint (elt);
|
|
|
|
cur = obj;
|
|
|
|
i = 0;
|
|
|
|
while (cur) {
|
|
|
|
if (i >= constraint) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i ++;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
if (i < constraint) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
|
|
"object has less values than defined: %ld",
|
|
|
|
(long int)constraint);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ucl_schema_validate (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, bool try_array,
|
|
|
|
struct ucl_schema_error *err,
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *external_refs)
|
2015-02-21 19:11:12 +01:00
|
|
|
{
|
2015-12-27 16:12:28 +01:00
|
|
|
const ucl_object_t *elt, *cur, *ref_root;
|
2015-02-21 19:11:12 +01:00
|
|
|
ucl_object_iter_t iter = NULL;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
if (schema->type != UCL_OBJECT) {
|
|
|
|
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
|
2015-12-27 16:12:28 +01:00
|
|
|
"schema is %s instead of object",
|
|
|
|
ucl_object_type_to_string (schema->type));
|
2015-02-21 19:11:12 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (try_array) {
|
|
|
|
/*
|
|
|
|
* Special case for multiple values
|
|
|
|
*/
|
|
|
|
if (!ucl_schema_validate_values (schema, obj, err)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
LL_FOREACH (obj, cur) {
|
2015-12-27 16:12:28 +01:00
|
|
|
if (!ucl_schema_validate (schema, cur, false, err, root, external_refs)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "enum");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
|
|
if (!ucl_schema_validate_enum (elt, obj, err)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "allOf");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
|
|
iter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
|
2015-02-21 19:11:12 +01:00
|
|
|
if (!ret) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "anyOf");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
|
|
iter = NULL;
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
|
2015-02-21 19:11:12 +01:00
|
|
|
if (ret) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Reset error */
|
|
|
|
err->code = UCL_SCHEMA_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "oneOf");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
|
|
iter = NULL;
|
|
|
|
ret = false;
|
2016-02-16 10:59:52 +01:00
|
|
|
while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
|
2015-02-21 19:11:12 +01:00
|
|
|
if (!ret) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|
2015-12-27 16:12:28 +01:00
|
|
|
else if (ucl_schema_validate (cur, obj, true, err, root, external_refs)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
ret = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "not");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL && elt->type == UCL_OBJECT) {
|
2015-12-27 16:12:28 +01:00
|
|
|
if (ucl_schema_validate (elt, obj, true, err, root, external_refs)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Reset error */
|
|
|
|
err->code = UCL_SCHEMA_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "$ref");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (elt != NULL) {
|
2015-12-27 16:12:28 +01:00
|
|
|
ref_root = root;
|
|
|
|
cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt),
|
|
|
|
err, external_refs, &ref_root);
|
|
|
|
|
2015-02-21 19:11:12 +01:00
|
|
|
if (cur == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-27 16:12:28 +01:00
|
|
|
if (!ucl_schema_validate (cur, obj, try_array, err, ref_root,
|
|
|
|
external_refs)) {
|
2015-02-21 19:11:12 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-16 10:59:52 +01:00
|
|
|
elt = ucl_object_lookup (schema, "type");
|
2015-02-21 19:11:12 +01:00
|
|
|
if (!ucl_schema_type_is_allowed (elt, obj, err)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (obj->type) {
|
|
|
|
case UCL_OBJECT:
|
2015-12-27 16:12:28 +01:00
|
|
|
return ucl_schema_validate_object (schema, obj, err, root, external_refs);
|
2015-02-21 19:11:12 +01:00
|
|
|
break;
|
|
|
|
case UCL_ARRAY:
|
2015-12-27 16:12:28 +01:00
|
|
|
return ucl_schema_validate_array (schema, obj, err, root, external_refs);
|
2015-02-21 19:11:12 +01:00
|
|
|
break;
|
|
|
|
case UCL_INT:
|
|
|
|
case UCL_FLOAT:
|
|
|
|
return ucl_schema_validate_number (schema, obj, err);
|
|
|
|
break;
|
|
|
|
case UCL_STRING:
|
|
|
|
return ucl_schema_validate_string (schema, obj, err);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ucl_object_validate (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
|
|
{
|
2015-12-27 16:12:28 +01:00
|
|
|
return ucl_object_validate_root_ext (schema, obj, schema, NULL, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ucl_object_validate_root (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj,
|
|
|
|
const ucl_object_t *root,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
return ucl_object_validate_root_ext (schema, obj, root, NULL, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ucl_object_validate_root_ext (const ucl_object_t *schema,
|
|
|
|
const ucl_object_t *obj,
|
|
|
|
const ucl_object_t *root,
|
|
|
|
ucl_object_t *ext_refs,
|
|
|
|
struct ucl_schema_error *err)
|
|
|
|
{
|
|
|
|
bool ret, need_unref = false;
|
|
|
|
|
|
|
|
if (ext_refs == NULL) {
|
|
|
|
ext_refs = ucl_object_typed_new (UCL_OBJECT);
|
|
|
|
need_unref = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ucl_schema_validate (schema, obj, true, err, root, ext_refs);
|
|
|
|
|
|
|
|
if (need_unref) {
|
|
|
|
ucl_object_unref (ext_refs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2015-02-21 19:11:12 +01:00
|
|
|
}
|