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