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);
-
-static bool
-ucl_string_to_type (const char *input, ucl_type_t *res)
-{
- if (strcasecmp (input, "object") == 0) {
- *res = UCL_OBJECT;
- }
- else if (strcasecmp (input, "array") == 0) {
- *res = UCL_ARRAY;
- }
- else if (strcasecmp (input, "integer") == 0) {
- *res = UCL_INT;
- }
- else if (strcasecmp (input, "number") == 0) {
- *res = UCL_FLOAT;
- }
- else if (strcasecmp (input, "string") == 0) {
- *res = UCL_STRING;
- }
- else if (strcasecmp (input, "boolean") == 0) {
- *res = UCL_BOOLEAN;
- }
- else if (strcasecmp (input, "null") == 0) {
- *res = UCL_NULL;
- }
- else {
- return false;
- }
-
- return true;
-}
-
-static const char *
-ucl_object_type_to_string (ucl_type_t type)
-{
- const char *res = "unknown";
-
- switch (type) {
- case UCL_OBJECT:
- res = "object";
- break;
- case UCL_ARRAY:
- res = "array";
- break;
- case UCL_INT:
- res = "integer";
- break;
- case UCL_FLOAT:
- case UCL_TIME:
- res = "number";
- break;
- case UCL_STRING:
- res = "string";
- break;
- case UCL_BOOLEAN:
- res = "boolean";
- break;
- case UCL_NULL:
- case UCL_USERDATA:
- res = "null";
- break;
- }
-
- return res;
-}
+ const ucl_object_t *root,
+ ucl_object_t *ext_ref);
/*
* Create validation error
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)
+ 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;
}
}
else if (cur->type == UCL_OBJECT) {
- ret = ucl_schema_validate (cur, obj, true, err, root);
+ ret = ucl_schema_validate (cur, obj, true, err, root, ext_ref);
}
}
}
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)
+ const ucl_object_t *root,
+ ucl_object_t *ext_ref)
{
const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
*required = NULL, *pat, *pelt;
while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
found = ucl_object_find_key (obj, ucl_object_key (prop));
if (found) {
- ret = ucl_schema_validate (prop, found, true, err, root);
+ ret = ucl_schema_validate (prop, found, true, err, root,
+ ext_ref);
}
}
}
while (ret && (prop = ucl_iterate_object (elt, &piter, true)) != NULL) {
found = ucl_schema_test_pattern (obj, ucl_object_key (prop));
if (found) {
- ret = ucl_schema_validate (prop, found, true, err, root);
+ 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);
+ ret = ucl_schema_validate_dependencies (elt, obj, err, root,
+ ext_ref);
}
}
break;
}
else if (additional_schema != NULL) {
- if (!ucl_schema_validate (additional_schema, elt, true, err, root)) {
+ if (!ucl_schema_validate (additional_schema, elt,
+ true, err, root, ext_ref)) {
ret = false;
break;
}
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)
+ const ucl_object_t *root,
+ ucl_object_t *ext_ref)
{
const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
*first_unvalidated = NULL;
found = ucl_array_head (obj);
while (ret && (it = ucl_iterate_object (elt, &piter, true)) != NULL) {
if (found) {
- ret = ucl_schema_validate (it, found, false, err, root);
+ ret = ucl_schema_validate (it, found, false, err,
+ root, ext_ref);
found = ucl_array_find_index (obj, ++idx);
}
}
else if (elt->type == UCL_OBJECT) {
/* Validate all items using the specified schema */
while (ret && (it = ucl_iterate_object (obj, &piter, true)) != NULL) {
- ret = ucl_schema_validate (elt, it, false, err, root);
+ ret = ucl_schema_validate (elt, it, false, err, root,
+ ext_ref);
}
}
else {
elt = ucl_array_find_index (obj, idx);
while (elt) {
if (!ucl_schema_validate (additional_schema, elt, false,
- err, root)) {
+ err, root, ext_ref)) {
ret = false;
break;
}
}
else if (type->type == UCL_STRING) {
type_str = ucl_object_tostring (type);
- if (!ucl_string_to_type (type_str, &t)) {
+ 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;
*/
static const ucl_object_t *
ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
- struct ucl_schema_error *err)
+ struct ucl_schema_error *err, ucl_object_t *ext_ref,
+ ucl_object_t const ** nroot)
{
- const char *p, *c;
- const ucl_object_t *res = NULL;
-
+ 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] != '#') {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
- "reference %s is invalid, not started with #", ref);
- return NULL;
+ 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_find_key (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)) {
+ free (url_copy);
+ ucl_schema_create_error (err,
+ UCL_SCHEMA_INVALID_SCHEMA,
+ root,
+ "cannot fetch reference %s: %s",
+ p,
+ url_err != NULL ? utstring_body (url_err)
+ : "unknown");
+ return NULL;
+ }
+ }
+ else {
+ if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err,
+ true)) {
+ free (url_copy);
+ ucl_schema_create_error (err,
+ UCL_SCHEMA_INVALID_SCHEMA,
+ root,
+ "cannot fetch reference %s: %s",
+ p,
+ url_err != NULL ? utstring_body (url_err)
+ : "unknown");
+ return NULL;
+ }
+ }
+
+ parser = ucl_parser_new (0);
+
+ if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) {
+ free (url_copy);
+ 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);
+ 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 = "";
+ }
}
- if (ref[1] == '/') {
- p = &ref[2];
+ else {
+ p = ref + 1;
}
- else if (ref[1] == '\0') {
- return root;
+
+ res = ext_obj != NULL ? ext_obj : root;
+ *nroot = res;
+
+ if (*p == '/') {
+ p++;
}
- else {
- ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
- "reference %s is invalid, not started with #/", ref);
- return NULL;
+ else if (*p == '\0') {
+ return res;
}
c = p;
- res = root;
while (*p != '\0') {
if (*p == '/') {
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)
+ const ucl_object_t *root,
+ ucl_object_t *external_refs)
{
- const ucl_object_t *elt, *cur;
+ 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));
+ "schema is %s instead of object",
+ ucl_object_type_to_string (schema->type));
return false;
}
return false;
}
LL_FOREACH (obj, cur) {
- if (!ucl_schema_validate (schema, cur, false, err, root)) {
+ if (!ucl_schema_validate (schema, cur, false, err, root, external_refs)) {
return false;
}
}
if (elt != NULL && elt->type == UCL_ARRAY) {
iter = NULL;
while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
- ret = ucl_schema_validate (cur, obj, true, err, root);
+ ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
if (!ret) {
return false;
}
if (elt != NULL && elt->type == UCL_ARRAY) {
iter = NULL;
while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
- ret = ucl_schema_validate (cur, obj, true, err, root);
+ ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
if (ret) {
break;
}
ret = false;
while ((cur = ucl_iterate_object (elt, &iter, true)) != NULL) {
if (!ret) {
- ret = ucl_schema_validate (cur, obj, true, err, root);
+ ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
}
- else if (ucl_schema_validate (cur, obj, true, err, root)) {
+ else if (ucl_schema_validate (cur, obj, true, err, root, external_refs)) {
ret = false;
break;
}
elt = ucl_object_find_key (schema, "not");
if (elt != NULL && elt->type == UCL_OBJECT) {
- if (ucl_schema_validate (elt, obj, true, err, root)) {
+ if (ucl_schema_validate (elt, obj, true, err, root, external_refs)) {
return false;
}
else {
elt = ucl_object_find_key (schema, "$ref");
if (elt != NULL) {
- cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt), err);
+ 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, root)) {
+ if (!ucl_schema_validate (cur, obj, try_array, err, ref_root,
+ external_refs)) {
return false;
}
}
switch (obj->type) {
case UCL_OBJECT:
- return ucl_schema_validate_object (schema, obj, err, root);
+ return ucl_schema_validate_object (schema, obj, err, root, external_refs);
break;
case UCL_ARRAY:
- return ucl_schema_validate_array (schema, obj, err, root);
+ return ucl_schema_validate_array (schema, obj, err, root, external_refs);
break;
case UCL_INT:
case UCL_FLOAT:
ucl_object_validate (const ucl_object_t *schema,
const ucl_object_t *obj, struct ucl_schema_error *err)
{
- return ucl_schema_validate (schema, obj, true, err, schema);
+ 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;
}