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