123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104 |
- /*
- * Copyright (c) 2014, Vsevolod Stakhov
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- #include "ucl.h"
- #include "ucl_internal.h"
- #include "tree.h"
- #include "utlist.h"
- #ifdef HAVE_STDARG_H
- #include <stdarg.h>
- #endif
- #ifdef HAVE_STDIO_H
- #include <stdio.h>
- #endif
- #ifdef HAVE_REGEX_H
- #include <regex.h>
- #endif
- #ifdef HAVE_MATH_H
- #include <math.h>
- #endif
-
- static bool ucl_schema_validate (const ucl_object_t *schema,
- const ucl_object_t *obj, bool try_array,
- struct ucl_schema_error *err,
- const ucl_object_t *root,
- ucl_object_t *ext_ref);
-
- /*
- * Create validation error
- */
-
- #ifdef __GNUC__
- static inline void
- ucl_schema_create_error (struct ucl_schema_error *err,
- enum ucl_schema_error_code code, const ucl_object_t *obj,
- const char *fmt, ...)
- __attribute__ (( format( printf, 4, 5) ));
- #endif
-
- static inline void
- ucl_schema_create_error (struct ucl_schema_error *err,
- enum ucl_schema_error_code code, const ucl_object_t *obj,
- const char *fmt, ...)
- {
- va_list va;
-
- if (err != NULL) {
- err->code = code;
- err->obj = obj;
- va_start (va, fmt);
- vsnprintf (err->msg, sizeof (err->msg), fmt, va);
- va_end (va);
- }
- }
-
- /*
- * Check whether we have a pattern specified
- */
- static const ucl_object_t *
- ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern, bool recursive)
- {
- const ucl_object_t *res = NULL;
- #ifdef HAVE_REGEX_H
- regex_t reg;
- const ucl_object_t *elt;
- ucl_object_iter_t iter = NULL;
-
- if (regcomp (®, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
- if (recursive) {
- while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
- if (regexec (®, ucl_object_key (elt), 0, NULL, 0) == 0) {
- res = elt;
- break;
- }
- }
- } else {
- if (regexec (®, ucl_object_key (obj), 0, NULL, 0) == 0)
- res = obj;
- }
- regfree (®);
- }
- #endif
- return res;
- }
-
- /*
- * Check dependencies for an object
- */
- static bool
- ucl_schema_validate_dependencies (const ucl_object_t *deps,
- const ucl_object_t *obj, struct ucl_schema_error *err,
- const ucl_object_t *root,
- ucl_object_t *ext_ref)
- {
- const ucl_object_t *elt, *cur, *cur_dep;
- ucl_object_iter_t iter = NULL, piter;
- bool ret = true;
-
- while (ret && (cur = ucl_object_iterate (deps, &iter, true)) != NULL) {
- elt = ucl_object_lookup (obj, ucl_object_key (cur));
- if (elt != NULL) {
- /* Need to check dependencies */
- if (cur->type == UCL_ARRAY) {
- piter = NULL;
- while (ret && (cur_dep = ucl_object_iterate (cur, &piter, true)) != NULL) {
- if (ucl_object_lookup (obj, ucl_object_tostring (cur_dep)) == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
- "dependency %s is missing for key %s",
- ucl_object_tostring (cur_dep), ucl_object_key (cur));
- ret = false;
- break;
- }
- }
- }
- else if (cur->type == UCL_OBJECT) {
- ret = ucl_schema_validate (cur, obj, true, err, root, ext_ref);
- }
- }
- }
-
- return ret;
- }
-
- /*
- * Validate object
- */
- static bool
- ucl_schema_validate_object (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err,
- const ucl_object_t *root,
- ucl_object_t *ext_ref)
- {
- const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
- *required = NULL, *pat, *pelt;
- ucl_object_iter_t iter = NULL, piter = NULL;
- bool ret = true, allow_additional = true;
- int64_t minmax;
-
- while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
- if (elt->type == UCL_OBJECT &&
- strcmp (ucl_object_key (elt), "properties") == 0) {
- piter = NULL;
- while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
- found = ucl_object_lookup (obj, ucl_object_key (prop));
- if (found) {
- ret = ucl_schema_validate (prop, found, true, err, root,
- ext_ref);
- }
- }
- }
- else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
- if (elt->type == UCL_BOOLEAN) {
- if (!ucl_object_toboolean (elt)) {
- /* Deny additional fields completely */
- allow_additional = false;
- }
- }
- else if (elt->type == UCL_OBJECT) {
- /* Define validator for additional fields */
- additional_schema = elt;
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "additionalProperties attribute is invalid in schema");
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "required") == 0) {
- if (elt->type == UCL_ARRAY) {
- required = elt;
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "required attribute is invalid in schema");
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "minProperties") == 0
- && ucl_object_toint_safe (elt, &minmax)) {
- if (obj->len < minmax) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object has not enough properties: %u, minimum is: %u",
- obj->len, (unsigned)minmax);
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
- && ucl_object_toint_safe (elt, &minmax)) {
- if (obj->len > minmax) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object has too many properties: %u, maximum is: %u",
- obj->len, (unsigned)minmax);
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
- const ucl_object_t *vobj;
- ucl_object_iter_t viter;
- piter = NULL;
- while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
- viter = NULL;
- while (ret && (vobj = ucl_object_iterate (obj, &viter, true)) != NULL) {
- found = ucl_schema_test_pattern (vobj, ucl_object_key (prop), false);
- if (found) {
- ret = ucl_schema_validate (prop, found, true, err, root,
- ext_ref);
- }
- }
- }
- }
- else if (elt->type == UCL_OBJECT &&
- strcmp (ucl_object_key (elt), "dependencies") == 0) {
- ret = ucl_schema_validate_dependencies (elt, obj, err, root,
- ext_ref);
- }
- }
-
- if (ret) {
- /* Additional properties */
- if (!allow_additional || additional_schema != NULL) {
- /* Check if we have exactly the same properties in schema and object */
- iter = ucl_object_iterate_new (obj);
- prop = ucl_object_lookup (schema, "properties");
- while ((elt = ucl_object_iterate_safe (iter, true)) != NULL) {
- found = ucl_object_lookup (prop, ucl_object_key (elt));
- if (found == NULL) {
- /* Try patternProperties */
- pat = ucl_object_lookup (schema, "patternProperties");
- piter = ucl_object_iterate_new (pat);
- while ((pelt = ucl_object_iterate_safe (piter, true)) != NULL) {
- found = ucl_schema_test_pattern (obj, ucl_object_key (pelt), true);
- if (found != NULL) {
- break;
- }
- }
- ucl_object_iterate_free (piter);
- piter = NULL;
- }
- if (found == NULL) {
- if (!allow_additional) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object has non-allowed property %s",
- ucl_object_key (elt));
- ret = false;
- break;
- }
- else if (additional_schema != NULL) {
- if (!ucl_schema_validate (additional_schema, elt,
- true, err, root, ext_ref)) {
- ret = false;
- break;
- }
- }
- }
- }
- ucl_object_iterate_free (iter);
- iter = NULL;
- }
- /* Required properties */
- if (required != NULL) {
- iter = NULL;
- while ((elt = ucl_object_iterate (required, &iter, true)) != NULL) {
- if (ucl_object_lookup (obj, ucl_object_tostring (elt)) == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
- "object has missing property %s",
- ucl_object_tostring (elt));
- ret = false;
- break;
- }
- }
- }
- }
-
-
- return ret;
- }
-
- static bool
- ucl_schema_validate_number (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err)
- {
- const ucl_object_t *elt, *test;
- ucl_object_iter_t iter = NULL;
- bool ret = true, exclusive = false;
- double constraint, val;
- const double alpha = 1e-16;
-
- while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
- if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
- strcmp (ucl_object_key (elt), "multipleOf") == 0) {
- constraint = ucl_object_todouble (elt);
- if (constraint <= 0) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "multipleOf must be greater than zero");
- ret = false;
- break;
- }
- val = ucl_object_todouble (obj);
- if (fabs (remainder (val, constraint)) > alpha) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "number %.4f is not multiple of %.4f, remainder is %.7f",
- val, constraint, remainder (val, constraint));
- ret = false;
- break;
- }
- }
- else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
- strcmp (ucl_object_key (elt), "maximum") == 0) {
- constraint = ucl_object_todouble (elt);
- test = ucl_object_lookup (schema, "exclusiveMaximum");
- if (test && test->type == UCL_BOOLEAN) {
- exclusive = ucl_object_toboolean (test);
- }
- val = ucl_object_todouble (obj);
- if (val > constraint || (exclusive && val >= constraint)) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "number is too big: %.3f, maximum is: %.3f",
- val, constraint);
- ret = false;
- break;
- }
- }
- else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
- strcmp (ucl_object_key (elt), "minimum") == 0) {
- constraint = ucl_object_todouble (elt);
- test = ucl_object_lookup (schema, "exclusiveMinimum");
- if (test && test->type == UCL_BOOLEAN) {
- exclusive = ucl_object_toboolean (test);
- }
- val = ucl_object_todouble (obj);
- if (val < constraint || (exclusive && val <= constraint)) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "number is too small: %.3f, minimum is: %.3f",
- val, constraint);
- ret = false;
- break;
- }
- }
- }
-
- return ret;
- }
-
- static bool
- ucl_schema_validate_string (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err)
- {
- const ucl_object_t *elt;
- ucl_object_iter_t iter = NULL;
- bool ret = true;
- int64_t constraint;
- #ifdef HAVE_REGEX_H
- regex_t re;
- #endif
-
- while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
- if (elt->type == UCL_INT &&
- strcmp (ucl_object_key (elt), "maxLength") == 0) {
- constraint = ucl_object_toint (elt);
- if (obj->len > constraint) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "string is too big: %u, maximum is: %" PRId64,
- obj->len, constraint);
- ret = false;
- break;
- }
- }
- else if (elt->type == UCL_INT &&
- strcmp (ucl_object_key (elt), "minLength") == 0) {
- constraint = ucl_object_toint (elt);
- if (obj->len < constraint) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "string is too short: %u, minimum is: %" PRId64,
- obj->len, constraint);
- ret = false;
- break;
- }
- }
- #ifdef HAVE_REGEX_H
- else if (elt->type == UCL_STRING &&
- strcmp (ucl_object_key (elt), "pattern") == 0) {
- if (regcomp (&re, ucl_object_tostring (elt),
- REG_EXTENDED | REG_NOSUB) != 0) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "cannot compile pattern %s", ucl_object_tostring (elt));
- ret = false;
- break;
- }
- if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "string doesn't match regexp %s",
- ucl_object_tostring (elt));
- ret = false;
- }
- regfree (&re);
- }
- #endif
- }
-
- return ret;
- }
-
- struct ucl_compare_node {
- const ucl_object_t *obj;
- TREE_ENTRY(ucl_compare_node) link;
- struct ucl_compare_node *next;
- };
-
- typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
-
- TREE_DEFINE(ucl_compare_node, link)
-
- static int
- ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
- {
- const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
-
- return ucl_object_compare (o1, o2);
- }
-
- static bool
- ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
- {
- ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
- ucl_object_iter_t iter = NULL;
- const ucl_object_t *elt;
- struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
- bool ret = true;
-
- while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
- test.obj = elt;
- node = TREE_FIND (&tree, ucl_compare_node, link, &test);
- if (node != NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
- "duplicate values detected while uniqueItems is true");
- ret = false;
- break;
- }
- node = calloc (1, sizeof (*node));
- if (node == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
- "cannot allocate tree node");
- ret = false;
- break;
- }
- node->obj = elt;
- TREE_INSERT (&tree, ucl_compare_node, link, node);
- LL_PREPEND (nodes, node);
- }
-
- LL_FOREACH_SAFE (nodes, node, tmp) {
- free (node);
- }
-
- return ret;
- }
-
- static bool
- ucl_schema_validate_array (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err,
- const ucl_object_t *root,
- ucl_object_t *ext_ref)
- {
- const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
- *first_unvalidated = NULL;
- ucl_object_iter_t iter = NULL, piter = NULL;
- bool ret = true, allow_additional = true, need_unique = false;
- int64_t minmax;
- unsigned int idx = 0;
-
- while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
- if (strcmp (ucl_object_key (elt), "items") == 0) {
- if (elt->type == UCL_ARRAY) {
- found = ucl_array_head (obj);
- while (ret && (it = ucl_object_iterate (elt, &piter, true)) != NULL) {
- if (found) {
- ret = ucl_schema_validate (it, found, false, err,
- root, ext_ref);
- found = ucl_array_find_index (obj, ++idx);
- }
- }
- if (found != NULL) {
- /* The first element that is not validated */
- first_unvalidated = found;
- }
- }
- else if (elt->type == UCL_OBJECT) {
- /* Validate all items using the specified schema */
- while (ret && (it = ucl_object_iterate (obj, &piter, true)) != NULL) {
- ret = ucl_schema_validate (elt, it, false, err, root,
- ext_ref);
- }
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "items attribute is invalid in schema");
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
- if (elt->type == UCL_BOOLEAN) {
- if (!ucl_object_toboolean (elt)) {
- /* Deny additional fields completely */
- allow_additional = false;
- }
- }
- else if (elt->type == UCL_OBJECT) {
- /* Define validator for additional fields */
- additional_schema = elt;
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
- "additionalItems attribute is invalid in schema");
- ret = false;
- break;
- }
- }
- else if (elt->type == UCL_BOOLEAN &&
- strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
- need_unique = ucl_object_toboolean (elt);
- }
- else if (strcmp (ucl_object_key (elt), "minItems") == 0
- && ucl_object_toint_safe (elt, &minmax)) {
- if (obj->len < minmax) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "array has not enough items: %u, minimum is: %u",
- obj->len, (unsigned)minmax);
- ret = false;
- break;
- }
- }
- else if (strcmp (ucl_object_key (elt), "maxItems") == 0
- && ucl_object_toint_safe (elt, &minmax)) {
- if (obj->len > minmax) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "array has too many items: %u, maximum is: %u",
- obj->len, (unsigned)minmax);
- ret = false;
- break;
- }
- }
- }
-
- if (ret) {
- /* Additional properties */
- if (!allow_additional || additional_schema != NULL) {
- if (first_unvalidated != NULL) {
- if (!allow_additional) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "array has undefined item");
- ret = false;
- }
- else if (additional_schema != NULL) {
- elt = ucl_array_find_index (obj, idx);
- while (elt) {
- if (!ucl_schema_validate (additional_schema, elt, false,
- err, root, ext_ref)) {
- ret = false;
- break;
- }
- elt = ucl_array_find_index (obj, idx ++);
- }
- }
- }
- }
- /* Required properties */
- if (ret && need_unique) {
- ret = ucl_schema_array_is_unique (obj, err);
- }
- }
-
- return ret;
- }
-
- /*
- * Returns whether this object is allowed for this type
- */
- static bool
- ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
- struct ucl_schema_error *err)
- {
- ucl_object_iter_t iter = NULL;
- const ucl_object_t *elt;
- const char *type_str;
- ucl_type_t t;
-
- if (type == NULL) {
- /* Any type is allowed */
- return true;
- }
-
- if (type->type == UCL_ARRAY) {
- /* One of allowed types */
- while ((elt = ucl_object_iterate (type, &iter, true)) != NULL) {
- if (ucl_schema_type_is_allowed (elt, obj, err)) {
- return true;
- }
- }
- }
- else if (type->type == UCL_STRING) {
- type_str = ucl_object_tostring (type);
- if (!ucl_object_string_to_type (type_str, &t)) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
- "Type attribute is invalid in schema");
- return false;
- }
- if (obj->type != t) {
- /* Some types are actually compatible */
- if (obj->type == UCL_TIME && t == UCL_FLOAT) {
- return true;
- }
- else if (obj->type == UCL_INT && t == UCL_FLOAT) {
- return true;
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
- "Invalid type of %s, expected %s",
- ucl_object_type_to_string (obj->type),
- ucl_object_type_to_string (t));
- }
- }
- else {
- /* Types are equal */
- return true;
- }
- }
-
- return false;
- }
-
- /*
- * Check if object is equal to one of elements of enum
- */
- static bool
- ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
- struct ucl_schema_error *err)
- {
- ucl_object_iter_t iter = NULL;
- const ucl_object_t *elt;
- bool ret = false;
-
- while ((elt = ucl_object_iterate (en, &iter, true)) != NULL) {
- if (ucl_object_compare (elt, obj) == 0) {
- ret = true;
- break;
- }
- }
-
- if (!ret) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object is not one of enumerated patterns");
- }
-
- return ret;
- }
-
-
- /*
- * Check a single ref component
- */
- static const ucl_object_t *
- ucl_schema_resolve_ref_component (const ucl_object_t *cur,
- const char *refc, int len,
- struct ucl_schema_error *err)
- {
- const ucl_object_t *res = NULL;
- char *err_str;
- int num, i;
-
- if (cur->type == UCL_OBJECT) {
- /* Find a key inside an object */
- res = ucl_object_lookup_len (cur, refc, len);
- if (res == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
- "reference %s is invalid, missing path component", refc);
- return NULL;
- }
- }
- else if (cur->type == UCL_ARRAY) {
- /* We must figure out a number inside array */
- num = strtoul (refc, &err_str, 10);
- if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
- "reference %s is invalid, invalid item number", refc);
- return NULL;
- }
- res = ucl_array_head (cur);
- i = 0;
- while (res != NULL) {
- if (i == num) {
- break;
- }
- res = res->next;
- }
- if (res == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
- "reference %s is invalid, item number %d does not exist",
- refc, num);
- return NULL;
- }
- }
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
- "reference %s is invalid, contains primitive object in the path",
- refc);
- return NULL;
- }
-
- return res;
- }
- /*
- * Find reference schema
- */
- static const ucl_object_t *
- ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
- struct ucl_schema_error *err, ucl_object_t *ext_ref,
- ucl_object_t const ** nroot)
- {
- UT_string *url_err = NULL;
- struct ucl_parser *parser;
- const ucl_object_t *res = NULL, *ext_obj = NULL;
- ucl_object_t *url_obj;
- const char *p, *c, *hash_ptr = NULL;
- char *url_copy = NULL;
- unsigned char *url_buf;
- size_t url_buflen;
-
- if (ref[0] != '#') {
- hash_ptr = strrchr (ref, '#');
-
- if (hash_ptr) {
- url_copy = malloc (hash_ptr - ref + 1);
-
- if (url_copy == NULL) {
- ucl_schema_create_error (err, UCL_SCHEMA_INTERNAL_ERROR, root,
- "cannot allocate memory");
- return NULL;
- }
-
- ucl_strlcpy (url_copy, ref, hash_ptr - ref + 1);
- p = url_copy;
- }
- else {
- /* Full URL */
- p = ref;
- }
-
- ext_obj = ucl_object_lookup (ext_ref, p);
-
- if (ext_obj == NULL) {
- if (ucl_strnstr (p, "://", strlen (p)) != NULL) {
- if (!ucl_fetch_url (p, &url_buf, &url_buflen, &url_err, true)) {
-
- ucl_schema_create_error (err,
- UCL_SCHEMA_INVALID_SCHEMA,
- root,
- "cannot fetch reference %s: %s",
- p,
- url_err != NULL ? utstring_body (url_err)
- : "unknown");
- free (url_copy);
-
- return NULL;
- }
- }
- else {
- if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err,
- true)) {
- ucl_schema_create_error (err,
- UCL_SCHEMA_INVALID_SCHEMA,
- root,
- "cannot fetch reference %s: %s",
- p,
- url_err != NULL ? utstring_body (url_err)
- : "unknown");
- free (url_copy);
-
- return NULL;
- }
- }
-
- parser = ucl_parser_new (0);
-
- if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
- "cannot fetch reference %s: %s", p,
- ucl_parser_get_error (parser));
- ucl_parser_free (parser);
- free (url_copy);
-
- return NULL;
- }
-
- url_obj = ucl_parser_get_object (parser);
- ext_obj = url_obj;
- ucl_object_insert_key (ext_ref, url_obj, p, 0, true);
- free (url_buf);
- }
-
- free (url_copy);
-
- if (hash_ptr) {
- p = hash_ptr + 1;
- }
- else {
- p = "";
- }
- }
- else {
- p = ref + 1;
- }
-
- res = ext_obj != NULL ? ext_obj : root;
- *nroot = res;
-
- if (*p == '/') {
- p++;
- }
- else if (*p == '\0') {
- return res;
- }
-
- c = p;
-
- while (*p != '\0') {
- if (*p == '/') {
- if (p - c == 0) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
- "reference %s is invalid, empty path component", ref);
- return NULL;
- }
- /* Now we have some url part, so we need to figure out where we are */
- res = ucl_schema_resolve_ref_component (res, c, p - c, err);
- if (res == NULL) {
- return NULL;
- }
- c = p + 1;
- }
- p ++;
- }
-
- if (p - c != 0) {
- res = ucl_schema_resolve_ref_component (res, c, p - c, err);
- }
-
- if (res == NULL || res->type != UCL_OBJECT) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
- "reference %s is invalid, cannot find specified object",
- ref);
- return NULL;
- }
-
- return res;
- }
-
- static bool
- ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
- struct ucl_schema_error *err)
- {
- const ucl_object_t *elt, *cur;
- int64_t constraint, i;
-
- elt = ucl_object_lookup (schema, "maxValues");
- if (elt != NULL && elt->type == UCL_INT) {
- constraint = ucl_object_toint (elt);
- cur = obj;
- i = 0;
- while (cur) {
- if (i > constraint) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object has more values than defined: %ld",
- (long int)constraint);
- return false;
- }
- i ++;
- cur = cur->next;
- }
- }
- elt = ucl_object_lookup (schema, "minValues");
- if (elt != NULL && elt->type == UCL_INT) {
- constraint = ucl_object_toint (elt);
- cur = obj;
- i = 0;
- while (cur) {
- if (i >= constraint) {
- break;
- }
- i ++;
- cur = cur->next;
- }
- if (i < constraint) {
- ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "object has less values than defined: %ld",
- (long int)constraint);
- return false;
- }
- }
-
- return true;
- }
-
- static bool
- ucl_schema_validate (const ucl_object_t *schema,
- const ucl_object_t *obj, bool try_array,
- struct ucl_schema_error *err,
- const ucl_object_t *root,
- ucl_object_t *external_refs)
- {
- const ucl_object_t *elt, *cur, *ref_root;
- ucl_object_iter_t iter = NULL;
- bool ret;
-
- if (schema->type != UCL_OBJECT) {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
- "schema is %s instead of object",
- ucl_object_type_to_string (schema->type));
- return false;
- }
-
- if (try_array) {
- /*
- * Special case for multiple values
- */
- if (!ucl_schema_validate_values (schema, obj, err)) {
- return false;
- }
- LL_FOREACH (obj, cur) {
- if (!ucl_schema_validate (schema, cur, false, err, root, external_refs)) {
- return false;
- }
- }
- return true;
- }
-
- elt = ucl_object_lookup (schema, "enum");
- if (elt != NULL && elt->type == UCL_ARRAY) {
- if (!ucl_schema_validate_enum (elt, obj, err)) {
- return false;
- }
- }
-
- elt = ucl_object_lookup (schema, "allOf");
- if (elt != NULL && elt->type == UCL_ARRAY) {
- iter = NULL;
- while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
- ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
- if (!ret) {
- return false;
- }
- }
- }
-
- elt = ucl_object_lookup (schema, "anyOf");
- if (elt != NULL && elt->type == UCL_ARRAY) {
- iter = NULL;
- while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
- ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
- if (ret) {
- break;
- }
- }
- if (!ret) {
- return false;
- }
- else {
- /* Reset error */
- err->code = UCL_SCHEMA_OK;
- }
- }
-
- elt = ucl_object_lookup (schema, "oneOf");
- if (elt != NULL && elt->type == UCL_ARRAY) {
- iter = NULL;
- ret = false;
- while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
- if (!ret) {
- ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
- }
- else if (ucl_schema_validate (cur, obj, true, err, root, external_refs)) {
- ret = false;
- break;
- }
- }
- if (!ret) {
- return false;
- }
- }
-
- elt = ucl_object_lookup (schema, "not");
- if (elt != NULL && elt->type == UCL_OBJECT) {
- if (ucl_schema_validate (elt, obj, true, err, root, external_refs)) {
- return false;
- }
- else {
- /* Reset error */
- err->code = UCL_SCHEMA_OK;
- }
- }
-
- elt = ucl_object_lookup (schema, "$ref");
- if (elt != NULL) {
- ref_root = root;
- cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt),
- err, external_refs, &ref_root);
-
- if (cur == NULL) {
- return false;
- }
- if (!ucl_schema_validate (cur, obj, try_array, err, ref_root,
- external_refs)) {
- return false;
- }
- }
-
- elt = ucl_object_lookup (schema, "type");
- if (!ucl_schema_type_is_allowed (elt, obj, err)) {
- return false;
- }
-
- switch (obj->type) {
- case UCL_OBJECT:
- return ucl_schema_validate_object (schema, obj, err, root, external_refs);
- break;
- case UCL_ARRAY:
- return ucl_schema_validate_array (schema, obj, err, root, external_refs);
- break;
- case UCL_INT:
- case UCL_FLOAT:
- return ucl_schema_validate_number (schema, obj, err);
- break;
- case UCL_STRING:
- return ucl_schema_validate_string (schema, obj, err);
- break;
- default:
- break;
- }
-
- return true;
- }
-
- bool
- ucl_object_validate (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err)
- {
- return ucl_object_validate_root_ext (schema, obj, schema, NULL, err);
- }
-
- bool
- ucl_object_validate_root (const ucl_object_t *schema,
- const ucl_object_t *obj,
- const ucl_object_t *root,
- struct ucl_schema_error *err)
- {
- return ucl_object_validate_root_ext (schema, obj, root, NULL, err);
- }
-
- bool
- ucl_object_validate_root_ext (const ucl_object_t *schema,
- const ucl_object_t *obj,
- const ucl_object_t *root,
- ucl_object_t *ext_refs,
- struct ucl_schema_error *err)
- {
- bool ret, need_unref = false;
-
- if (ext_refs == NULL) {
- ext_refs = ucl_object_typed_new (UCL_OBJECT);
- need_unref = true;
- }
-
- ret = ucl_schema_validate (schema, obj, true, err, root, ext_refs);
-
- if (need_unref) {
- ucl_object_unref (ext_refs);
- }
-
- return ret;
- }
|