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