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