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 } 99 else { 100 if (regexec(®, ucl_object_key(obj), 0, NULL, 0) == 0) 101 res = obj; 102 } 103 regfree(®); 104 } 105 #endif 106 return res; 107 } 108 109 /* 110 * Check dependencies for an object 111 */ 112 static bool 113 ucl_schema_validate_dependencies(const ucl_object_t *deps, 114 const ucl_object_t *obj, struct ucl_schema_error *err, 115 const ucl_object_t *root, 116 ucl_object_t *ext_ref) 117 { 118 const ucl_object_t *elt, *cur, *cur_dep; 119 ucl_object_iter_t iter = NULL, piter; 120 bool ret = true; 121 122 while (ret && (cur = ucl_object_iterate(deps, &iter, true)) != NULL) { 123 elt = ucl_object_lookup(obj, ucl_object_key(cur)); 124 if (elt != NULL) { 125 /* Need to check dependencies */ 126 if (cur->type == UCL_ARRAY) { 127 piter = NULL; 128 while (ret && (cur_dep = ucl_object_iterate(cur, &piter, true)) != NULL) { 129 if (ucl_object_lookup(obj, ucl_object_tostring(cur_dep)) == NULL) { 130 ucl_schema_create_error(err, UCL_SCHEMA_MISSING_DEPENDENCY, elt, 131 "dependency %s is missing for key %s", 132 ucl_object_tostring(cur_dep), ucl_object_key(cur)); 133 ret = false; 134 break; 135 } 136 } 137 } 138 else if (cur->type == UCL_OBJECT) { 139 ret = ucl_schema_validate(cur, obj, true, err, root, ext_ref); 140 } 141 } 142 } 143 144 return ret; 145 } 146 147 /* 148 * Validate object 149 */ 150 static bool 151 ucl_schema_validate_object(const ucl_object_t *schema, 152 const ucl_object_t *obj, struct ucl_schema_error *err, 153 const ucl_object_t *root, 154 ucl_object_t *ext_ref) 155 { 156 const ucl_object_t *elt, *prop, *found, *additional_schema = NULL, 157 *required = NULL, *pat, *pelt; 158 ucl_object_iter_t iter = NULL, piter = NULL; 159 bool ret = true, allow_additional = true; 160 int64_t minmax; 161 162 while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) { 163 if (elt->type == UCL_OBJECT && 164 strcmp(ucl_object_key(elt), "properties") == 0) { 165 piter = NULL; 166 while (ret && (prop = ucl_object_iterate(elt, &piter, true)) != NULL) { 167 found = ucl_object_lookup(obj, ucl_object_key(prop)); 168 if (found) { 169 ret = ucl_schema_validate(prop, found, true, err, root, 170 ext_ref); 171 } 172 } 173 } 174 else if (strcmp(ucl_object_key(elt), "additionalProperties") == 0) { 175 if (elt->type == UCL_BOOLEAN) { 176 if (!ucl_object_toboolean(elt)) { 177 /* Deny additional fields completely */ 178 allow_additional = false; 179 } 180 } 181 else if (elt->type == UCL_OBJECT) { 182 /* Define validator for additional fields */ 183 additional_schema = elt; 184 } 185 else { 186 ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt, 187 "additionalProperties attribute is invalid in schema"); 188 ret = false; 189 break; 190 } 191 } 192 else if (strcmp(ucl_object_key(elt), "required") == 0) { 193 if (elt->type == UCL_ARRAY) { 194 required = elt; 195 } 196 else { 197 ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt, 198 "required attribute is invalid in schema"); 199 ret = false; 200 break; 201 } 202 } 203 else if (strcmp(ucl_object_key(elt), "minProperties") == 0 && 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 && 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) 431 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 { 479 free(node); 480 } 481 482 return ret; 483 } 484 485 static bool 486 ucl_schema_validate_array(const ucl_object_t *schema, 487 const ucl_object_t *obj, struct ucl_schema_error *err, 488 const ucl_object_t *root, 489 ucl_object_t *ext_ref) 490 { 491 const ucl_object_t *elt, *it, *found, *additional_schema = NULL, 492 *first_unvalidated = NULL; 493 ucl_object_iter_t iter = NULL, piter = NULL; 494 bool ret = true, allow_additional = true, need_unique = false; 495 int64_t minmax; 496 unsigned int idx = 0; 497 498 while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) { 499 if (strcmp(ucl_object_key(elt), "items") == 0) { 500 if (elt->type == UCL_ARRAY) { 501 found = ucl_array_head(obj); 502 while (ret && (it = ucl_object_iterate(elt, &piter, true)) != NULL) { 503 if (found) { 504 ret = ucl_schema_validate(it, found, false, err, 505 root, ext_ref); 506 found = ucl_array_find_index(obj, ++idx); 507 } 508 } 509 if (found != NULL) { 510 /* The first element that is not validated */ 511 first_unvalidated = found; 512 } 513 } 514 else if (elt->type == UCL_OBJECT) { 515 /* Validate all items using the specified schema */ 516 while (ret && (it = ucl_object_iterate(obj, &piter, true)) != NULL) { 517 ret = ucl_schema_validate(elt, it, false, err, root, 518 ext_ref); 519 } 520 } 521 else { 522 ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt, 523 "items attribute is invalid in schema"); 524 ret = false; 525 break; 526 } 527 } 528 else if (strcmp(ucl_object_key(elt), "additionalItems") == 0) { 529 if (elt->type == UCL_BOOLEAN) { 530 if (!ucl_object_toboolean(elt)) { 531 /* Deny additional fields completely */ 532 allow_additional = false; 533 } 534 } 535 else if (elt->type == UCL_OBJECT) { 536 /* Define validator for additional fields */ 537 additional_schema = elt; 538 } 539 else { 540 ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt, 541 "additionalItems attribute is invalid in schema"); 542 ret = false; 543 break; 544 } 545 } 546 else if (elt->type == UCL_BOOLEAN && 547 strcmp(ucl_object_key(elt), "uniqueItems") == 0) { 548 need_unique = ucl_object_toboolean(elt); 549 } 550 else if (strcmp(ucl_object_key(elt), "minItems") == 0 && 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 && 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 { 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 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 ucl_object_validate_root(const ucl_object_t *schema, 1075 const ucl_object_t *obj, 1076 const ucl_object_t *root, 1077 struct ucl_schema_error *err) 1078 { 1079 return ucl_object_validate_root_ext(schema, obj, root, NULL, err); 1080 } 1081 1082 bool ucl_object_validate_root_ext(const ucl_object_t *schema, 1083 const ucl_object_t *obj, 1084 const ucl_object_t *root, 1085 ucl_object_t *ext_refs, 1086 struct ucl_schema_error *err) 1087 { 1088 bool ret, need_unref = false; 1089 1090 if (ext_refs == NULL) { 1091 ext_refs = ucl_object_typed_new(UCL_OBJECT); 1092 need_unref = true; 1093 } 1094 1095 ret = ucl_schema_validate(schema, obj, true, err, root, ext_refs); 1096 1097 if (need_unref) { 1098 ucl_object_unref(ext_refs); 1099 } 1100 1101 return ret; 1102 } 1103