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