xref: /openbmc/libcper/tests/schemavalidator/validate.c (revision 6df70ff6085cdb28decb39cc04160a7edc28beee)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <json-c/json.h>
6 
7 #include "validate.h"
8 
9 json_object *json = NULL;
10 json_object *schema = NULL;
11 json_object *defs = NULL;
12 
13 static char *schemavalidator_errstr[SCHEMAVALIDATOR_ERR_MAX] = {
14 	"VALID",
15 	"GENERAL ERROR",
16 	"JSON FILE NOT FOUND",
17 	"SCHEMA FILE NOT FOUND",
18 	"WRONG ARGUEMNTS GIVEN",
19 	"SCHEMA ERROR",
20 	"INVALID",
21 	"REGEX MISMATCH",
22 	"REGEX MATCH",
23 	"REGEX COMPILE FAILED"
24 };
25 
schemavalidator_errorstr(unsigned int schemavalidator_errors)26 const char *schemavalidator_errorstr(unsigned int schemavalidator_errors)
27 {
28 	if (schemavalidator_errors < SCHEMAVALIDATOR_ERR_MAX) {
29 		return schemavalidator_errstr[schemavalidator_errors];
30 	}
31 	return NULL;
32 }
33 
_schemavalidator_load(const char * jsonfile,const char * jsonschema)34 int _schemavalidator_load(const char *jsonfile, const char *jsonschema)
35 {
36 	json = json_object_from_file(jsonfile);
37 	if (json == NULL) {
38 		return SCHEMAVALIDATOR_ERR_JSON_NOT_FOUND;
39 	}
40 
41 	schema = json_object_from_file(jsonschema);
42 	if (schema == NULL) {
43 		json_object_put(json);
44 		return SCHEMAVALIDATOR_ERR_SCHEMA_NOT_FOUND;
45 	}
46 
47 	return SCHEMAVALIDATOR_ERR_VALID;
48 }
49 
__schemavalidator_inspect_type(json_object * jobj,const char * type,json_object * joutput_node)50 int __schemavalidator_inspect_type(json_object *jobj, const char *type,
51 				   json_object *joutput_node)
52 {
53 	if (strcmp(type, "object") == 0) {
54 		if (json_object_is_type(jobj, json_type_object)) {
55 			return SCHEMAVALIDATOR_ERR_VALID;
56 		}
57 	} else if (strcmp(type, "array") == 0) {
58 		if (json_object_is_type(jobj, json_type_array)) {
59 			return SCHEMAVALIDATOR_ERR_VALID;
60 		}
61 	} else if (strcmp(type, "string") == 0) {
62 		if (json_object_is_type(jobj, json_type_string)) {
63 			return SCHEMAVALIDATOR_ERR_VALID;
64 		}
65 	} else if (strcmp(type, "integer") == 0) {
66 		if (json_object_is_type(jobj, json_type_int)) {
67 			return SCHEMAVALIDATOR_ERR_VALID;
68 		}
69 		if (json_object_is_type(jobj, json_type_double)) {
70 			double value = json_object_get_double(jobj);
71 			if (value ==
72 			    round(value)) { // "zero fractional part is an integer"
73 				return SCHEMAVALIDATOR_ERR_VALID;
74 			}
75 		}
76 	} else if (strcmp(type, "double") == 0) {
77 		if (json_object_is_type(jobj, json_type_double)) {
78 			return SCHEMAVALIDATOR_ERR_VALID;
79 		}
80 	} else if (strcmp(type, "number") == 0) {
81 		if (json_object_is_type(jobj, json_type_double) ||
82 		    json_object_is_type(jobj, json_type_int)) {
83 			return SCHEMAVALIDATOR_ERR_VALID;
84 		}
85 	} else if (strcmp(type, "boolean") == 0) {
86 		if (json_object_is_type(jobj, json_type_boolean)) {
87 			return SCHEMAVALIDATOR_ERR_VALID;
88 		}
89 	} else if (strcmp(type, "null") == 0) {
90 		if (json_object_is_type(jobj, json_type_null)) {
91 			return SCHEMAVALIDATOR_ERR_VALID;
92 		}
93 	} else {
94 		printf("WARN unknown type in check type %s\n", type);
95 		json_object *jnode =
96 			_schemavalidator_output_create_and_append_node(
97 				joutput_node, "type");
98 		_schemavalidator_output_apply_result(
99 			jnode, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
100 		return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
101 	}
102 	json_object *jnode = _schemavalidator_output_create_and_append_node(
103 		joutput_node, "type");
104 	_schemavalidator_output_apply_result(jnode,
105 					     SCHEMAVALIDATOR_ERR_INVALID);
106 
107 	return SCHEMAVALIDATOR_ERR_INVALID;
108 }
109 
schemavalidator_check_bool(json_object * jobj,json_object * jschema,json_object * joutput_node)110 int schemavalidator_check_bool(json_object *jobj, json_object *jschema,
111 			       json_object *joutput_node)
112 {
113 	(void)jobj;
114 	// check if jschema is a bool, true or false
115 	int err;
116 	if (json_object_is_type(jschema, json_type_boolean)) {
117 		json_object *jnode =
118 			_schemavalidator_output_create_and_append_node(
119 				joutput_node, "bool");
120 		json_bool value = json_object_get_boolean(jschema);
121 		err = value == 0 ? SCHEMAVALIDATOR_ERR_INVALID :
122 				   SCHEMAVALIDATOR_ERR_VALID;
123 		_schemavalidator_output_apply_result(jnode, err);
124 		return err;
125 	}
126 	return SCHEMAVALIDATOR_ERR_VALID;
127 }
128 
_schemavalidator_check_type(json_object * jobj,json_object * jschema,json_object * joutput_node)129 int _schemavalidator_check_type(json_object *jobj, json_object *jschema,
130 				json_object *joutput_node)
131 {
132 	json_object *jnode;
133 	json_object *jtype = json_object_object_get(jschema, "type");
134 	if (jtype == NULL) {
135 		return SCHEMAVALIDATOR_ERR_VALID;
136 	}
137 	if (json_object_is_type(jtype, json_type_string)) {
138 		const char *type = json_object_get_string(jtype);
139 		return __schemavalidator_inspect_type(jobj, type, joutput_node);
140 	}
141 	if (json_object_is_type(jtype, json_type_array)) {
142 		int arraylen = json_object_array_length(jtype);
143 		for (int i = 0; i < arraylen; i++) {
144 			json_object *iobj = json_object_array_get_idx(jtype, i);
145 			if (!json_object_is_type(iobj, json_type_string)) {
146 				goto check_type_schema_error;
147 			}
148 			const char *type = json_object_get_string(iobj);
149 			int err = __schemavalidator_inspect_type(jobj, type,
150 								 joutput_node);
151 			if (err == SCHEMAVALIDATOR_ERR_VALID) {
152 				return SCHEMAVALIDATOR_ERR_VALID;
153 			}
154 		}
155 		jnode = _schemavalidator_output_create_and_append_node(
156 			joutput_node, "type");
157 		_schemavalidator_output_apply_result(
158 			jnode, SCHEMAVALIDATOR_ERR_INVALID);
159 		return SCHEMAVALIDATOR_ERR_INVALID;
160 	}
161 check_type_schema_error:
162 	jnode = _schemavalidator_output_create_and_append_node(joutput_node,
163 							       "type");
164 	_schemavalidator_output_apply_result(jnode,
165 					     SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
166 	return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
167 }
168 
_schemavalidator_check_required(json_object * jobj,json_object * jschema,json_object * joutput_node)169 int _schemavalidator_check_required(json_object *jobj, json_object *jschema,
170 				    json_object *joutput_node)
171 {
172 	//printf("%s\n%s\n", __func__, json_object_to_json_string(jobj));
173 	json_object *jarray = json_object_object_get(jschema, "required");
174 	if (!jarray) {
175 		return SCHEMAVALIDATOR_ERR_VALID;
176 	}
177 
178 	json_object *jrequired_node =
179 		_schemavalidator_output_create_and_append_node(joutput_node,
180 							       "required");
181 	int missing_required_key = 0;
182 
183 	int arraylen = json_object_array_length(jarray);
184 	for (int i = 0; i < arraylen; i++) {
185 		json_object *iobj = json_object_array_get_idx(jarray, i);
186 		const char *key = json_object_get_string(iobj);
187 		if (key) {
188 			//printf("%s\n", key);
189 			// use json_object_object_get_ex becuase of json_type_null types
190 			json_object *required_object = NULL;
191 			int err = json_object_object_get_ex(jobj, key,
192 							    &required_object);
193 			if (err == 0) {
194 				// printf("required key missing: %s\n", key);
195 				json_object *jkeynode =
196 					_schemavalidator_output_create_and_append_node(
197 						jrequired_node, key);
198 				_schemavalidator_output_apply_result(
199 					jkeynode, SCHEMAVALIDATOR_ERR_INVALID);
200 				missing_required_key = 1;
201 			}
202 		}
203 	}
204 	int ret = missing_required_key == 1 ? SCHEMAVALIDATOR_ERR_INVALID :
205 					      SCHEMAVALIDATOR_ERR_VALID;
206 	_schemavalidator_output_apply_result(jrequired_node, ret);
207 	return ret;
208 }
209 
_schemavalidator_check_properties(json_object * jobj,json_object * jschema,json_object * joutput_node)210 int _schemavalidator_check_properties(json_object *jobj, json_object *jschema,
211 				      json_object *joutput_node)
212 {
213 	// printf("%s\n", __func__);
214 
215 	json_object *jprops = json_object_object_get(jschema, "properties");
216 	if (!jprops) {
217 		return SCHEMAVALIDATOR_ERR_VALID;
218 	}
219 
220 	json_object *jproperties_node =
221 		_schemavalidator_output_create_and_append_node(joutput_node,
222 							       "properties");
223 	int properties_valid = 1;
224 	json_object_object_foreach(jprops, jprop_key, jprop_val)
225 	{
226 		// printf("key of prop is %s\n", jprop_key);
227 		json_object *iobj = json_object_object_get(jobj, jprop_key);
228 		//printf("iobj %s type %d\nkey %s\nval %s\n", json_object_get_string(iobj), json_object_get_type(iobj), jprop_key, json_object_get_string(jprop_val));
229 		if (iobj) {
230 			json_object *jprop_item_tmp_node =
231 				_schemavalidator_output_create_node(jprop_key);
232 			int err = _schemavalidator_validate_instance(
233 				iobj, jprop_val, jprop_item_tmp_node);
234 			if (err != SCHEMAVALIDATOR_ERR_VALID) {
235 				properties_valid = 0;
236 				_schemavalidator_output_apply_result(
237 					jprop_item_tmp_node, err);
238 				_schemavalidator_output_append_node(
239 					jproperties_node, jprop_item_tmp_node);
240 			} else {
241 				json_object_put(jprop_item_tmp_node);
242 			}
243 		}
244 	}
245 	int ret = properties_valid == 1 ? SCHEMAVALIDATOR_ERR_VALID :
246 					  SCHEMAVALIDATOR_ERR_INVALID;
247 	_schemavalidator_output_apply_result(jproperties_node, ret);
248 	return ret;
249 }
250 
_schemavalidator_check_prefixItems_and_items(json_object * jobj,json_object * jschema,json_object * joutput_node)251 int _schemavalidator_check_prefixItems_and_items(json_object *jobj,
252 						 json_object *jschema,
253 						 json_object *joutput_node)
254 {
255 	json_object *jprefixitems =
256 		json_object_object_get(jschema, "prefixItems");
257 	json_object *jitems = json_object_object_get(jschema, "items");
258 
259 	int prefixitems_ok = 1;
260 	int items_ok = 1;
261 
262 	if (jprefixitems) {
263 		json_object *jprefixitems_node =
264 			_schemavalidator_output_create_and_append_node(
265 				joutput_node, "prefixItems");
266 
267 		if (!json_object_is_type(jprefixitems, json_type_array)) {
268 			_schemavalidator_output_apply_result(
269 				jprefixitems_node,
270 				SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
271 			return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
272 		}
273 
274 		int jobj_arraylen = json_object_array_length(jobj);
275 		int prefixitems_arraylen =
276 			json_object_array_length(jprefixitems);
277 		for (int i = 0; i < jobj_arraylen && i < prefixitems_arraylen;
278 		     i++) {
279 			//printf("i=%d prefixitems\n", i);
280 			json_object *iobj = json_object_array_get_idx(jobj, i);
281 			json_object *ischema =
282 				json_object_array_get_idx(jprefixitems, i);
283 
284 			char numstr[12];
285 			snprintf(numstr, sizeof(numstr), "%d", i);
286 			json_object *jarrayitem_tmp_node =
287 				_schemavalidator_output_create_node(numstr);
288 
289 			int err = _schemavalidator_validate_instance(
290 				iobj, ischema, jarrayitem_tmp_node);
291 			if (err) {
292 				_schemavalidator_output_apply_result(
293 					jprefixitems_node, err);
294 				_schemavalidator_output_append_node(
295 					jprefixitems_node, jarrayitem_tmp_node);
296 				prefixitems_ok = 0;
297 			} else {
298 				json_object_put(jarrayitem_tmp_node);
299 			}
300 		}
301 		int prefixitems_ret = (prefixitems_ok == 1) ?
302 					      SCHEMAVALIDATOR_ERR_VALID :
303 					      SCHEMAVALIDATOR_ERR_INVALID;
304 		_schemavalidator_output_apply_result(jprefixitems_node,
305 						     prefixitems_ret);
306 	}
307 
308 	if (jitems) {
309 		json_object *jitems_node =
310 			_schemavalidator_output_create_and_append_node(
311 				joutput_node, "items");
312 
313 		if (!json_object_is_type(jitems, json_type_object) &&
314 		    !json_object_is_type(jitems, json_type_boolean)) {
315 			_schemavalidator_output_apply_result(
316 				jitems_node, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
317 			return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
318 		}
319 
320 		int jobj_arraylen = json_object_array_length(jobj);
321 		int items_arraylen = 0;
322 		for (int i = items_arraylen; i < jobj_arraylen; i++) {
323 			//printf("i=%d items\n", i);
324 			json_object *iobj = json_object_array_get_idx(jobj, i);
325 			char numstr[12];
326 			snprintf(numstr, sizeof(numstr), "%d", i);
327 			json_object *jarrayitem_tmp_node =
328 				_schemavalidator_output_create_node(numstr);
329 			int err = _schemavalidator_validate_instance(
330 				iobj, jitems, jarrayitem_tmp_node);
331 			if (err) {
332 				_schemavalidator_output_apply_result(
333 					jarrayitem_tmp_node, err);
334 				_schemavalidator_output_append_node(
335 					jitems_node, jarrayitem_tmp_node);
336 				items_ok = 0;
337 			} else {
338 				json_object_put(jarrayitem_tmp_node);
339 			}
340 		}
341 		int items_ret = (items_ok == 1) ? SCHEMAVALIDATOR_ERR_VALID :
342 						  SCHEMAVALIDATOR_ERR_INVALID;
343 		_schemavalidator_output_apply_result(jitems_node, items_ret);
344 	}
345 	int ret = (prefixitems_ok == 1 && items_ok == 1) ?
346 			  SCHEMAVALIDATOR_ERR_VALID :
347 			  SCHEMAVALIDATOR_ERR_INVALID;
348 	return ret;
349 }
350 
_schemavalidator_value_is_equal(json_object * jobj1,json_object * jobj2)351 int _schemavalidator_value_is_equal(json_object *jobj1, json_object *jobj2)
352 {
353 	if (json_object_equal(jobj1, jobj2)) {
354 		return SCHEMAVALIDATOR_ERR_VALID;
355 	}
356 
357 	if (json_object_is_type(jobj1, json_type_double) &&
358 	    json_object_is_type(jobj2, json_type_int)) {
359 		double value = json_object_get_double(jobj1);
360 		double value2 = json_object_get_int64(jobj2);
361 		if (value == round(value) && value == value2) {
362 			return SCHEMAVALIDATOR_ERR_VALID;
363 		}
364 	}
365 
366 	if (json_object_is_type(jobj1, json_type_int) &&
367 	    json_object_is_type(jobj2, json_type_double)) {
368 		double value = json_object_get_double(jobj2);
369 		double value2 = json_object_get_int64(jobj1);
370 		if (value == round(value) && value == value2) {
371 			return SCHEMAVALIDATOR_ERR_VALID;
372 		}
373 	}
374 
375 	return SCHEMAVALIDATOR_ERR_INVALID;
376 }
377 
_schemavalidator_check_const(json_object * jobj,json_object * jschema,json_object * joutput_node)378 int _schemavalidator_check_const(json_object *jobj, json_object *jschema,
379 				 json_object *joutput_node)
380 {
381 	json_object *jconst;
382 	int err = json_object_object_get_ex(jschema, "const", &jconst);
383 	if (err == 0) {
384 		return SCHEMAVALIDATOR_ERR_VALID;
385 	}
386 
387 	err = _schemavalidator_value_is_equal(jobj, jconst);
388 	if (err == SCHEMAVALIDATOR_ERR_VALID) {
389 		return SCHEMAVALIDATOR_ERR_VALID;
390 	}
391 
392 	json_object *jnode = _schemavalidator_output_create_and_append_node(
393 		joutput_node, "const");
394 	_schemavalidator_output_apply_result(jnode,
395 					     SCHEMAVALIDATOR_ERR_INVALID);
396 	return SCHEMAVALIDATOR_ERR_INVALID;
397 }
398 
_schemavalidator_check_enums(json_object * jobj,json_object * jschema,json_object * joutput_node)399 int _schemavalidator_check_enums(json_object *jobj, json_object *jschema,
400 				 json_object *joutput_node)
401 {
402 	json_object *jenum_array = json_object_object_get(jschema, "enum");
403 
404 	if (!jenum_array) {
405 		return SCHEMAVALIDATOR_ERR_VALID;
406 	}
407 
408 	if (!json_object_is_type(jenum_array, json_type_array)) {
409 		json_object *jnode =
410 			_schemavalidator_output_create_and_append_node(
411 				joutput_node, "enum");
412 		_schemavalidator_output_apply_result(
413 			jnode, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
414 		return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
415 	}
416 
417 	int arraylen = json_object_array_length(jenum_array);
418 	for (int i = 0; i < arraylen; i++) {
419 		json_object *ienum = json_object_array_get_idx(jenum_array, i);
420 		int err = _schemavalidator_value_is_equal(jobj, ienum);
421 		if (err == SCHEMAVALIDATOR_ERR_VALID) {
422 			return SCHEMAVALIDATOR_ERR_VALID;
423 		}
424 	}
425 	// printf("ERROR: enum check failed (%s not in enum)\n", json_object_to_json_string(jobj));
426 
427 	json_object *jnode = _schemavalidator_output_create_and_append_node(
428 		joutput_node, "enum");
429 	_schemavalidator_output_apply_result(jnode,
430 					     SCHEMAVALIDATOR_ERR_INVALID);
431 	return SCHEMAVALIDATOR_ERR_INVALID;
432 }
433 
_schemavalidator_check_uniqueItems(json_object * jobj,json_object * jschema,json_object * joutput_node)434 int _schemavalidator_check_uniqueItems(json_object *jobj, json_object *jschema,
435 				       json_object *joutput_node)
436 {
437 	json_object *juniq = json_object_object_get(jschema, "uniqueItems");
438 	if (!juniq) {
439 		return SCHEMAVALIDATOR_ERR_VALID;
440 	}
441 
442 	json_object *juniqueitems_node =
443 		_schemavalidator_output_create_and_append_node(joutput_node,
444 							       "uniqueItems");
445 
446 	if (!json_object_is_type(juniq, json_type_boolean)) {
447 		_schemavalidator_output_apply_result(
448 			juniqueitems_node, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
449 		return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
450 	}
451 
452 	// uniqueItems=false is valid
453 	if (json_object_get_boolean(juniq) == 0) {
454 		_schemavalidator_output_apply_result(juniqueitems_node,
455 						     SCHEMAVALIDATOR_ERR_VALID);
456 		return SCHEMAVALIDATOR_ERR_VALID;
457 	}
458 
459 	int uniqueitems_ok = 1;
460 	int arraylen = json_object_array_length(jobj);
461 	for (int i = 0; i < arraylen - 1; i++) {
462 		json_object *iobj = json_object_array_get_idx(jobj, i);
463 		for (int j = i + 1; j < arraylen; j++) {
464 			json_object *uobj = json_object_array_get_idx(jobj, j);
465 			if (json_object_equal(iobj, uobj) == 1) {
466 				uniqueitems_ok = 0;
467 				char numstr[12];
468 				snprintf(numstr, sizeof(numstr), "%d", i);
469 				json_object *jnotunique_node =
470 					_schemavalidator_output_create_and_append_node(
471 						juniqueitems_node, numstr);
472 				_schemavalidator_output_apply_result(
473 					jnotunique_node,
474 					SCHEMAVALIDATOR_ERR_INVALID);
475 			}
476 		}
477 	}
478 	int ret = uniqueitems_ok == 1 ? SCHEMAVALIDATOR_ERR_VALID :
479 					SCHEMAVALIDATOR_ERR_INVALID;
480 	_schemavalidator_output_apply_result(juniqueitems_node, ret);
481 	return ret;
482 }
483 
_schemavalidator_check_maxmin_items(json_object * jobj,json_object * jschema,json_object * joutput_node)484 int _schemavalidator_check_maxmin_items(json_object *jobj, json_object *jschema,
485 					json_object *joutput_node)
486 {
487 	int err = SCHEMAVALIDATOR_ERR_VALID;
488 	int arraylen = json_object_array_length(jobj);
489 
490 	json_object *jmax = json_object_object_get(jschema, "maxItems");
491 	if (jmax) {
492 		if (json_object_is_type(jmax, json_type_int) ||
493 		    json_object_is_type(jmax, json_type_double)) {
494 			int maxitems = json_object_get_double(jmax);
495 			if (arraylen > maxitems) {
496 				json_object *jmaxitems_node =
497 					_schemavalidator_output_create_and_append_node(
498 						joutput_node, "maxItems");
499 				_schemavalidator_output_apply_result(
500 					jmaxitems_node,
501 					SCHEMAVALIDATOR_ERR_INVALID);
502 				err = SCHEMAVALIDATOR_ERR_INVALID;
503 			}
504 		}
505 	}
506 
507 	json_object *jmin = json_object_object_get(jschema, "minItems");
508 	if (jmin) {
509 		if (json_object_is_type(jmin, json_type_int) ||
510 		    json_object_is_type(jmin, json_type_double)) {
511 			int minitems = json_object_get_double(jmin);
512 			if (arraylen < minitems) {
513 				json_object *jminitems_node =
514 					_schemavalidator_output_create_and_append_node(
515 						joutput_node, "minItems");
516 				_schemavalidator_output_apply_result(
517 					jminitems_node,
518 					SCHEMAVALIDATOR_ERR_INVALID);
519 				err = SCHEMAVALIDATOR_ERR_INVALID;
520 			}
521 		}
522 	}
523 
524 	// if (err)
525 	//     printf("ERROR: failed at maxItems or minItems check\n");
526 	return err;
527 }
528 
_schemavalidator_validate_array(json_object * jobj,json_object * jschema,json_object * joutput_node)529 int _schemavalidator_validate_array(json_object *jobj, json_object *jschema,
530 				    json_object *joutput_node)
531 {
532 	int err;
533 
534 	err = _schemavalidator_check_prefixItems_and_items(jobj, jschema,
535 							   joutput_node);
536 	if (err) {
537 		return err;
538 	}
539 
540 	err = _schemavalidator_check_uniqueItems(jobj, jschema, joutput_node);
541 	if (err) {
542 		return err;
543 	}
544 
545 	err = _schemavalidator_check_maxmin_items(jobj, jschema, joutput_node);
546 	if (err) {
547 		return err;
548 	}
549 
550 	return SCHEMAVALIDATOR_ERR_VALID;
551 }
552 
_schemavalidator_validate_object(json_object * jobj,json_object * jschema,json_object * joutput_node)553 int _schemavalidator_validate_object(json_object *jobj, json_object *jschema,
554 				     json_object *joutput_node)
555 {
556 	int err;
557 	if (defs == NULL) {
558 		defs = json_object_object_get(jschema, "$defs");
559 	}
560 
561 	err = _schemavalidator_check_required(jobj, jschema, joutput_node);
562 	if (err) {
563 		return err;
564 	}
565 
566 	err = _schemavalidator_check_properties(jobj, jschema, joutput_node);
567 	if (err) {
568 		return err;
569 	}
570 
571 	return SCHEMAVALIDATOR_ERR_VALID;
572 }
573 
utf8_length(const char * str)574 int utf8_length(const char *str)
575 {
576 	const char *pointer = str;
577 	int len = 0;
578 	while (pointer[0]) {
579 		if ((pointer[0] & 0xC0) != 0x80) {
580 			len++;
581 		}
582 		pointer++;
583 	}
584 	return len;
585 }
586 
_schemavalidator_validate_string(json_object * jobj,json_object * jschema,json_object * joutput_node)587 int _schemavalidator_validate_string(json_object *jobj, json_object *jschema,
588 				     json_object *joutput_node)
589 {
590 	const char *str = json_object_get_string(jobj);
591 	//printf("strlen of %s %ld %d %d\n", str, strlen(str), json_object_get_string_len(jobj), utf8_length(str));
592 
593 	int minlength_ok = 1;
594 	json_object *jminlen = json_object_object_get(jschema, "minLength");
595 	if (jminlen) {
596 		int minlen = json_object_get_int64(jminlen);
597 		if (utf8_length(str) < minlen) {
598 			minlength_ok = 0;
599 			json_object *jminlength_node =
600 				_schemavalidator_output_create_and_append_node(
601 					joutput_node, "minLength");
602 			_schemavalidator_output_apply_result(
603 				jminlength_node, SCHEMAVALIDATOR_ERR_INVALID);
604 		}
605 	}
606 
607 	int maxlength_ok = 1;
608 	json_object *jmaxlen = json_object_object_get(jschema, "maxLength");
609 	if (jmaxlen) {
610 		int maxlen = json_object_get_int64(jmaxlen);
611 		if (utf8_length(str) > maxlen) {
612 			maxlength_ok = 0;
613 			json_object *jmaxlength_node =
614 				_schemavalidator_output_create_and_append_node(
615 					joutput_node, "maxLength");
616 			_schemavalidator_output_apply_result(
617 				jmaxlength_node, SCHEMAVALIDATOR_ERR_INVALID);
618 		}
619 	}
620 
621 	int enums_ok = 1;
622 	int err = _schemavalidator_check_enums(jobj, jschema, joutput_node);
623 	if (err) {
624 		if (err == SCHEMAVALIDATOR_ERR_SCHEMA_ERROR) {
625 			// _schemavalidator_output_apply_result(joutput_node, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
626 			return err;
627 		}
628 		enums_ok = 0;
629 	}
630 
631 	int ret = minlength_ok == 1 && maxlength_ok == 1 && enums_ok == 1 ?
632 			  SCHEMAVALIDATOR_ERR_VALID :
633 			  SCHEMAVALIDATOR_ERR_INVALID;
634 	// _schemavalidator_output_apply_result(joutput_node, ret);
635 	return ret;
636 }
637 
_schemavalidator_validate_integer(json_object * jobj,json_object * jschema,json_object * joutput_node)638 int _schemavalidator_validate_integer(json_object *jobj, json_object *jschema,
639 				      json_object *joutput_node)
640 {
641 	(void)jobj;
642 	double value = (double)json_object_get_int64(jobj);
643 	int err = _schemavalidator_validate_number(jobj, jschema, value,
644 						   joutput_node);
645 	return err;
646 }
647 
_schemavalidator_validate_double(json_object * jobj,json_object * jschema,json_object * joutput_node)648 int _schemavalidator_validate_double(json_object *jobj, json_object *jschema,
649 				     json_object *joutput_node)
650 {
651 	(void)jobj;
652 	double value = json_object_get_double(jobj);
653 	int err = _schemavalidator_validate_number(jobj, jschema, value,
654 						   joutput_node);
655 	return err;
656 }
657 
_schemavalidator_validate_number(json_object * jobj,json_object * jschema,double value,json_object * joutput_node)658 int _schemavalidator_validate_number(json_object *jobj, json_object *jschema,
659 				     double value, json_object *joutput_node)
660 {
661 	(void)jobj;
662 	int multipleOf_ok = 1;
663 	json_object *jmult = json_object_object_get(jschema, "multipleOf");
664 	if (jmult) {
665 		double multipland = (double)json_object_get_double(jmult);
666 		if (multipland == 0.0) {
667 			json_object *jmultipleOf_node =
668 				_schemavalidator_output_create_and_append_node(
669 					joutput_node, "multipleOf");
670 			_schemavalidator_output_apply_result(
671 				jmultipleOf_node,
672 				SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
673 			// _schemavalidator_output_apply_result(joutput_node, SCHEMAVALIDATOR_ERR_SCHEMA_ERROR);
674 			return SCHEMAVALIDATOR_ERR_SCHEMA_ERROR;
675 		}
676 
677 		double divided = value / multipland;
678 		if (isinf(divided) != 0 || divided != round(divided)) {
679 			multipleOf_ok = 0;
680 		}
681 		if (multipleOf_ok == 0) {
682 			json_object *jmultipleOf_node =
683 				_schemavalidator_output_create_and_append_node(
684 					joutput_node, "multipleOf");
685 			_schemavalidator_output_apply_result(
686 				jmultipleOf_node, SCHEMAVALIDATOR_ERR_INVALID);
687 		}
688 	}
689 
690 	int minimum_ok = 1;
691 	json_object *jmin = json_object_object_get(jschema, "minimum");
692 	if (jmin) {
693 		double min = (double)json_object_get_double(jmin);
694 		if (value < min) {
695 			minimum_ok = 0;
696 			json_object *jminimum_node =
697 				_schemavalidator_output_create_and_append_node(
698 					joutput_node, "minimum");
699 			_schemavalidator_output_apply_result(
700 				jminimum_node, SCHEMAVALIDATOR_ERR_INVALID);
701 		}
702 	}
703 
704 	int exclusiveMinimum_ok = 1;
705 	json_object *jexclmin =
706 		json_object_object_get(jschema, "exclusiveMinimum");
707 	if (jexclmin) {
708 		double min = (double)json_object_get_double(jexclmin);
709 		if (value <= min) {
710 			exclusiveMinimum_ok = 0;
711 			json_object *jexclusiveMinimum_node =
712 				_schemavalidator_output_create_and_append_node(
713 					joutput_node, "exclusiveMinimum");
714 			_schemavalidator_output_apply_result(
715 				jexclusiveMinimum_node,
716 				SCHEMAVALIDATOR_ERR_INVALID);
717 		}
718 	}
719 
720 	int maximum_ok = 1;
721 	json_object *jmax = json_object_object_get(jschema, "maximum");
722 	if (jmax) {
723 		double max = (double)json_object_get_double(jmax);
724 		if (value > max) {
725 			maximum_ok = 0;
726 			json_object *jmaximum_node =
727 				_schemavalidator_output_create_and_append_node(
728 					joutput_node, "maximum");
729 			_schemavalidator_output_apply_result(
730 				jmaximum_node, SCHEMAVALIDATOR_ERR_INVALID);
731 		}
732 	}
733 
734 	int exclusiveMaximum_ok = 1;
735 	json_object *jexclmax =
736 		json_object_object_get(jschema, "exclusiveMaximum");
737 	if (jexclmax) {
738 		double max = (double)json_object_get_double(jexclmax);
739 		if (value >= max) {
740 			exclusiveMaximum_ok = 0;
741 			json_object *jexclusiveMaximum_node =
742 				_schemavalidator_output_create_and_append_node(
743 					joutput_node, "exclusiveMaximum");
744 			_schemavalidator_output_apply_result(
745 				jexclusiveMaximum_node,
746 				SCHEMAVALIDATOR_ERR_INVALID);
747 		}
748 	}
749 	int ret = multipleOf_ok == 1 && minimum_ok == 1 &&
750 				  exclusiveMinimum_ok == 1 && maximum_ok == 1 &&
751 				  exclusiveMaximum_ok == 1 ?
752 			  SCHEMAVALIDATOR_ERR_VALID :
753 			  SCHEMAVALIDATOR_ERR_INVALID;
754 	// _schemavalidator_output_apply_result(joutput_node, ret);
755 	return ret;
756 }
757 
_schemavalidator_validate_boolean(json_object * jobj,json_object * jschema,json_object * joutput_node)758 int _schemavalidator_validate_boolean(json_object *jobj, json_object *jschema,
759 				      json_object *joutput_node)
760 {
761 	(void)jobj;
762 	(void)jschema;
763 	(void)joutput_node;
764 	// printf("%s\n", __func__);
765 	// _schemavalidator_output_apply_result(joutput_node, SCHEMAVALIDATOR_ERR_VALID);
766 	return SCHEMAVALIDATOR_ERR_VALID;
767 }
768 
_schemavalidator_validate_instance(json_object * jobj,json_object * jschema,json_object * joutput_node)769 int _schemavalidator_validate_instance(json_object *jobj, json_object *jschema,
770 				       json_object *joutput_node)
771 {
772 	int err;
773 	// printf("--validate instance--\n");
774 	// printf("%s\n", json_object_get_string(jobj));
775 	// printf("%s\n", json_object_get_string(jschema));
776 
777 	err = schemavalidator_check_bool(jobj, jschema, joutput_node);
778 	if (err) {
779 		return err;
780 	}
781 
782 	err = _schemavalidator_check_type(jobj, jschema, joutput_node);
783 	if (err) {
784 		return err;
785 	}
786 
787 	err = _schemavalidator_check_const(jobj, jschema, joutput_node);
788 	if (err) {
789 		return err;
790 	}
791 
792 	err = _schemavalidator_check_enums(jobj, jschema, joutput_node);
793 	if (err) {
794 		return err;
795 	}
796 
797 	json_type type = json_object_get_type(jobj);
798 
799 	if (type == json_type_object) {
800 		return _schemavalidator_validate_object(jobj, jschema,
801 							joutput_node);
802 	}
803 	if (type == json_type_array) {
804 		return _schemavalidator_validate_array(jobj, jschema,
805 						       joutput_node);
806 	}
807 	if (type == json_type_string) {
808 		return _schemavalidator_validate_string(jobj, jschema,
809 							joutput_node);
810 	}
811 	if (type == json_type_boolean) {
812 		return _schemavalidator_validate_boolean(jobj, jschema,
813 							 joutput_node);
814 	}
815 	if (type == json_type_int) {
816 		return _schemavalidator_validate_integer(jobj, jschema,
817 							 joutput_node);
818 	}
819 	if (type == json_type_double) {
820 		return _schemavalidator_validate_double(jobj, jschema,
821 							joutput_node);
822 	}
823 	if (type == json_type_null) {
824 		return SCHEMAVALIDATOR_ERR_VALID;
825 	}
826 	printf("%s: WARN: type %d not handled\n", __func__, type);
827 
828 	return SCHEMAVALIDATOR_ERR_VALID;
829 }
830 
schemavalidator_validate(json_object * jobj,json_object * jschema)831 int schemavalidator_validate(json_object *jobj, json_object *jschema)
832 {
833 	json_object *joutput = _schemavalidator_output_create_node("root");
834 	int err = _schemavalidator_validate_instance(jobj, jschema, joutput);
835 	_schemavalidator_output_apply_result(joutput, err);
836 
837 	if (joutput) {
838 		//printf("Basic Output: %s\n", json_object_get_string(joutput));
839 		_schemavalidator_output_print_errors(joutput);
840 	}
841 
842 	if (joutput) {
843 		json_object_put(joutput);
844 	}
845 	return err;
846 }
847