Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/libucl/src/ucl_schema.c
2066 views
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 (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
91
if (recursive) {
92
while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
93
if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
94
res = elt;
95
break;
96
}
97
}
98
} else {
99
if (regexec (&reg, ucl_object_key (obj), 0, NULL, 0) == 0)
100
res = obj;
101
}
102
regfree (&reg);
103
}
104
#endif
105
return res;
106
}
107
108
/*
109
* Check dependencies for an object
110
*/
111
static bool
112
ucl_schema_validate_dependencies (const ucl_object_t *deps,
113
const ucl_object_t *obj, struct ucl_schema_error *err,
114
const ucl_object_t *root,
115
ucl_object_t *ext_ref)
116
{
117
const ucl_object_t *elt, *cur, *cur_dep;
118
ucl_object_iter_t iter = NULL, piter;
119
bool ret = true;
120
121
while (ret && (cur = ucl_object_iterate (deps, &iter, true)) != NULL) {
122
elt = ucl_object_lookup (obj, ucl_object_key (cur));
123
if (elt != NULL) {
124
/* Need to check dependencies */
125
if (cur->type == UCL_ARRAY) {
126
piter = NULL;
127
while (ret && (cur_dep = ucl_object_iterate (cur, &piter, true)) != NULL) {
128
if (ucl_object_lookup (obj, ucl_object_tostring (cur_dep)) == NULL) {
129
ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
130
"dependency %s is missing for key %s",
131
ucl_object_tostring (cur_dep), ucl_object_key (cur));
132
ret = false;
133
break;
134
}
135
}
136
}
137
else if (cur->type == UCL_OBJECT) {
138
ret = ucl_schema_validate (cur, obj, true, err, root, ext_ref);
139
}
140
}
141
}
142
143
return ret;
144
}
145
146
/*
147
* Validate object
148
*/
149
static bool
150
ucl_schema_validate_object (const ucl_object_t *schema,
151
const ucl_object_t *obj, struct ucl_schema_error *err,
152
const ucl_object_t *root,
153
ucl_object_t *ext_ref)
154
{
155
const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
156
*required = NULL, *pat, *pelt;
157
ucl_object_iter_t iter = NULL, piter = NULL;
158
bool ret = true, allow_additional = true;
159
int64_t minmax;
160
161
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
162
if (elt->type == UCL_OBJECT &&
163
strcmp (ucl_object_key (elt), "properties") == 0) {
164
piter = NULL;
165
while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
166
found = ucl_object_lookup (obj, ucl_object_key (prop));
167
if (found) {
168
ret = ucl_schema_validate (prop, found, true, err, root,
169
ext_ref);
170
}
171
}
172
}
173
else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
174
if (elt->type == UCL_BOOLEAN) {
175
if (!ucl_object_toboolean (elt)) {
176
/* Deny additional fields completely */
177
allow_additional = false;
178
}
179
}
180
else if (elt->type == UCL_OBJECT) {
181
/* Define validator for additional fields */
182
additional_schema = elt;
183
}
184
else {
185
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
186
"additionalProperties attribute is invalid in schema");
187
ret = false;
188
break;
189
}
190
}
191
else if (strcmp (ucl_object_key (elt), "required") == 0) {
192
if (elt->type == UCL_ARRAY) {
193
required = elt;
194
}
195
else {
196
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
197
"required attribute is invalid in schema");
198
ret = false;
199
break;
200
}
201
}
202
else if (strcmp (ucl_object_key (elt), "minProperties") == 0
203
&& 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
213
&& ucl_object_toint_safe (elt, &minmax)) {
214
if (obj->len > minmax) {
215
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
216
"object has too many properties: %u, maximum is: %u",
217
obj->len, (unsigned)minmax);
218
ret = false;
219
break;
220
}
221
}
222
else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
223
const ucl_object_t *vobj;
224
ucl_object_iter_t viter;
225
piter = NULL;
226
while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
227
viter = NULL;
228
while (ret && (vobj = ucl_object_iterate (obj, &viter, true)) != NULL) {
229
found = ucl_schema_test_pattern (vobj, ucl_object_key (prop), false);
230
if (found) {
231
ret = ucl_schema_validate (prop, found, true, err, root,
232
ext_ref);
233
}
234
}
235
}
236
}
237
else if (elt->type == UCL_OBJECT &&
238
strcmp (ucl_object_key (elt), "dependencies") == 0) {
239
ret = ucl_schema_validate_dependencies (elt, obj, err, root,
240
ext_ref);
241
}
242
}
243
244
if (ret) {
245
/* Additional properties */
246
if (!allow_additional || additional_schema != NULL) {
247
/* Check if we have exactly the same properties in schema and object */
248
iter = ucl_object_iterate_new (obj);
249
prop = ucl_object_lookup (schema, "properties");
250
while ((elt = ucl_object_iterate_safe (iter, true)) != NULL) {
251
found = ucl_object_lookup (prop, ucl_object_key (elt));
252
if (found == NULL) {
253
/* Try patternProperties */
254
pat = ucl_object_lookup (schema, "patternProperties");
255
piter = ucl_object_iterate_new (pat);
256
while ((pelt = ucl_object_iterate_safe (piter, true)) != NULL) {
257
found = ucl_schema_test_pattern (obj, ucl_object_key (pelt), true);
258
if (found != NULL) {
259
break;
260
}
261
}
262
ucl_object_iterate_free (piter);
263
piter = NULL;
264
}
265
if (found == NULL) {
266
if (!allow_additional) {
267
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
268
"object has non-allowed property %s",
269
ucl_object_key (elt));
270
ret = false;
271
break;
272
}
273
else if (additional_schema != NULL) {
274
if (!ucl_schema_validate (additional_schema, elt,
275
true, err, root, ext_ref)) {
276
ret = false;
277
break;
278
}
279
}
280
}
281
}
282
ucl_object_iterate_free (iter);
283
iter = NULL;
284
}
285
/* Required properties */
286
if (required != NULL) {
287
iter = NULL;
288
while ((elt = ucl_object_iterate (required, &iter, true)) != NULL) {
289
if (ucl_object_lookup (obj, ucl_object_tostring (elt)) == NULL) {
290
ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
291
"object has missing property %s",
292
ucl_object_tostring (elt));
293
ret = false;
294
break;
295
}
296
}
297
}
298
}
299
300
301
return ret;
302
}
303
304
static bool
305
ucl_schema_validate_number (const ucl_object_t *schema,
306
const ucl_object_t *obj, struct ucl_schema_error *err)
307
{
308
const ucl_object_t *elt, *test;
309
ucl_object_iter_t iter = NULL;
310
bool ret = true, exclusive = false;
311
double constraint, val;
312
const double alpha = 1e-16;
313
314
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
315
if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
316
strcmp (ucl_object_key (elt), "multipleOf") == 0) {
317
constraint = ucl_object_todouble (elt);
318
if (constraint <= 0) {
319
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
320
"multipleOf must be greater than zero");
321
ret = false;
322
break;
323
}
324
val = ucl_object_todouble (obj);
325
if (fabs (remainder (val, constraint)) > alpha) {
326
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
327
"number %.4f is not multiple of %.4f, remainder is %.7f",
328
val, constraint, remainder (val, constraint));
329
ret = false;
330
break;
331
}
332
}
333
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
334
strcmp (ucl_object_key (elt), "maximum") == 0) {
335
constraint = ucl_object_todouble (elt);
336
test = ucl_object_lookup (schema, "exclusiveMaximum");
337
if (test && test->type == UCL_BOOLEAN) {
338
exclusive = ucl_object_toboolean (test);
339
}
340
val = ucl_object_todouble (obj);
341
if (val > constraint || (exclusive && val >= constraint)) {
342
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
343
"number is too big: %.3f, maximum is: %.3f",
344
val, constraint);
345
ret = false;
346
break;
347
}
348
}
349
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
350
strcmp (ucl_object_key (elt), "minimum") == 0) {
351
constraint = ucl_object_todouble (elt);
352
test = ucl_object_lookup (schema, "exclusiveMinimum");
353
if (test && test->type == UCL_BOOLEAN) {
354
exclusive = ucl_object_toboolean (test);
355
}
356
val = ucl_object_todouble (obj);
357
if (val < constraint || (exclusive && val <= constraint)) {
358
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
359
"number is too small: %.3f, minimum is: %.3f",
360
val, constraint);
361
ret = false;
362
break;
363
}
364
}
365
}
366
367
return ret;
368
}
369
370
static bool
371
ucl_schema_validate_string (const ucl_object_t *schema,
372
const ucl_object_t *obj, struct ucl_schema_error *err)
373
{
374
const ucl_object_t *elt;
375
ucl_object_iter_t iter = NULL;
376
bool ret = true;
377
int64_t constraint;
378
#ifdef HAVE_REGEX_H
379
regex_t re;
380
#endif
381
382
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
383
if (elt->type == UCL_INT &&
384
strcmp (ucl_object_key (elt), "maxLength") == 0) {
385
constraint = ucl_object_toint (elt);
386
if (obj->len > constraint) {
387
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
388
"string is too big: %u, maximum is: %" PRId64,
389
obj->len, constraint);
390
ret = false;
391
break;
392
}
393
}
394
else if (elt->type == UCL_INT &&
395
strcmp (ucl_object_key (elt), "minLength") == 0) {
396
constraint = ucl_object_toint (elt);
397
if (obj->len < constraint) {
398
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
399
"string is too short: %u, minimum is: %" PRId64,
400
obj->len, constraint);
401
ret = false;
402
break;
403
}
404
}
405
#ifdef HAVE_REGEX_H
406
else if (elt->type == UCL_STRING &&
407
strcmp (ucl_object_key (elt), "pattern") == 0) {
408
if (regcomp (&re, ucl_object_tostring (elt),
409
REG_EXTENDED | REG_NOSUB) != 0) {
410
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
411
"cannot compile pattern %s", ucl_object_tostring (elt));
412
ret = false;
413
break;
414
}
415
if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
416
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
417
"string doesn't match regexp %s",
418
ucl_object_tostring (elt));
419
ret = false;
420
}
421
regfree (&re);
422
}
423
#endif
424
}
425
426
return ret;
427
}
428
429
struct ucl_compare_node {
430
const ucl_object_t *obj;
431
TREE_ENTRY(ucl_compare_node) 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
free (node);
479
}
480
481
return ret;
482
}
483
484
static bool
485
ucl_schema_validate_array (const ucl_object_t *schema,
486
const ucl_object_t *obj, struct ucl_schema_error *err,
487
const ucl_object_t *root,
488
ucl_object_t *ext_ref)
489
{
490
const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
491
*first_unvalidated = NULL;
492
ucl_object_iter_t iter = NULL, piter = NULL;
493
bool ret = true, allow_additional = true, need_unique = false;
494
int64_t minmax;
495
unsigned int idx = 0;
496
497
while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
498
if (strcmp (ucl_object_key (elt), "items") == 0) {
499
if (elt->type == UCL_ARRAY) {
500
found = ucl_array_head (obj);
501
while (ret && (it = ucl_object_iterate (elt, &piter, true)) != NULL) {
502
if (found) {
503
ret = ucl_schema_validate (it, found, false, err,
504
root, ext_ref);
505
found = ucl_array_find_index (obj, ++idx);
506
}
507
}
508
if (found != NULL) {
509
/* The first element that is not validated */
510
first_unvalidated = found;
511
}
512
}
513
else if (elt->type == UCL_OBJECT) {
514
/* Validate all items using the specified schema */
515
while (ret && (it = ucl_object_iterate (obj, &piter, true)) != NULL) {
516
ret = ucl_schema_validate (elt, it, false, err, root,
517
ext_ref);
518
}
519
}
520
else {
521
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
522
"items attribute is invalid in schema");
523
ret = false;
524
break;
525
}
526
}
527
else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
528
if (elt->type == UCL_BOOLEAN) {
529
if (!ucl_object_toboolean (elt)) {
530
/* Deny additional fields completely */
531
allow_additional = false;
532
}
533
}
534
else if (elt->type == UCL_OBJECT) {
535
/* Define validator for additional fields */
536
additional_schema = elt;
537
}
538
else {
539
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
540
"additionalItems attribute is invalid in schema");
541
ret = false;
542
break;
543
}
544
}
545
else if (elt->type == UCL_BOOLEAN &&
546
strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
547
need_unique = ucl_object_toboolean (elt);
548
}
549
else if (strcmp (ucl_object_key (elt), "minItems") == 0
550
&& 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
560
&& ucl_object_toint_safe (elt, &minmax)) {
561
if (obj->len > minmax) {
562
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
563
"array has too many items: %u, maximum is: %u",
564
obj->len, (unsigned)minmax);
565
ret = false;
566
break;
567
}
568
}
569
}
570
571
if (ret) {
572
/* Additional properties */
573
if (!allow_additional || additional_schema != NULL) {
574
if (first_unvalidated != NULL) {
575
if (!allow_additional) {
576
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
577
"array has undefined item");
578
ret = false;
579
}
580
else if (additional_schema != NULL) {
581
elt = ucl_array_find_index (obj, idx);
582
while (elt) {
583
if (!ucl_schema_validate (additional_schema, elt, false,
584
err, root, ext_ref)) {
585
ret = false;
586
break;
587
}
588
elt = ucl_array_find_index (obj, idx ++);
589
}
590
}
591
}
592
}
593
/* Required properties */
594
if (ret && need_unique) {
595
ret = ucl_schema_array_is_unique (obj, err);
596
}
597
}
598
599
return ret;
600
}
601
602
/*
603
* Returns whether this object is allowed for this type
604
*/
605
static bool
606
ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
607
struct ucl_schema_error *err)
608
{
609
ucl_object_iter_t iter = NULL;
610
const ucl_object_t *elt;
611
const char *type_str;
612
ucl_type_t t;
613
614
if (type == NULL) {
615
/* Any type is allowed */
616
return true;
617
}
618
619
if (type->type == UCL_ARRAY) {
620
/* One of allowed types */
621
while ((elt = ucl_object_iterate (type, &iter, true)) != NULL) {
622
if (ucl_schema_type_is_allowed (elt, obj, err)) {
623
return true;
624
}
625
}
626
}
627
else if (type->type == UCL_STRING) {
628
type_str = ucl_object_tostring (type);
629
if (!ucl_object_string_to_type (type_str, &t)) {
630
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
631
"Type attribute is invalid in schema");
632
return false;
633
}
634
if (obj->type != t) {
635
/* Some types are actually compatible */
636
if (obj->type == UCL_TIME && t == UCL_FLOAT) {
637
return true;
638
}
639
else if (obj->type == UCL_INT && t == UCL_FLOAT) {
640
return true;
641
}
642
else {
643
ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
644
"Invalid type of %s, expected %s",
645
ucl_object_type_to_string (obj->type),
646
ucl_object_type_to_string (t));
647
}
648
}
649
else {
650
/* Types are equal */
651
return true;
652
}
653
}
654
655
return false;
656
}
657
658
/*
659
* Check if object is equal to one of elements of enum
660
*/
661
static bool
662
ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
663
struct ucl_schema_error *err)
664
{
665
ucl_object_iter_t iter = NULL;
666
const ucl_object_t *elt;
667
bool ret = false;
668
669
while ((elt = ucl_object_iterate (en, &iter, true)) != NULL) {
670
if (ucl_object_compare (elt, obj) == 0) {
671
ret = true;
672
break;
673
}
674
}
675
676
if (!ret) {
677
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
678
"object is not one of enumerated patterns");
679
}
680
681
return ret;
682
}
683
684
685
/*
686
* Check a single ref component
687
*/
688
static const ucl_object_t *
689
ucl_schema_resolve_ref_component (const ucl_object_t *cur,
690
const char *refc, int len,
691
struct ucl_schema_error *err)
692
{
693
const ucl_object_t *res = NULL;
694
char *err_str;
695
int num, i;
696
697
if (cur->type == UCL_OBJECT) {
698
/* Find a key inside an object */
699
res = ucl_object_lookup_len (cur, refc, len);
700
if (res == NULL) {
701
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
702
"reference %s is invalid, missing path component", refc);
703
return NULL;
704
}
705
}
706
else if (cur->type == UCL_ARRAY) {
707
/* We must figure out a number inside array */
708
num = strtoul (refc, &err_str, 10);
709
if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
710
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
711
"reference %s is invalid, invalid item number", refc);
712
return NULL;
713
}
714
res = ucl_array_head (cur);
715
i = 0;
716
while (res != NULL) {
717
if (i == num) {
718
break;
719
}
720
res = res->next;
721
}
722
if (res == NULL) {
723
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
724
"reference %s is invalid, item number %d does not exist",
725
refc, num);
726
return NULL;
727
}
728
}
729
else {
730
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
731
"reference %s is invalid, contains primitive object in the path",
732
refc);
733
return NULL;
734
}
735
736
return res;
737
}
738
/*
739
* Find reference schema
740
*/
741
static const ucl_object_t *
742
ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
743
struct ucl_schema_error *err, ucl_object_t *ext_ref,
744
ucl_object_t const ** nroot)
745
{
746
UT_string *url_err = NULL;
747
struct ucl_parser *parser;
748
const ucl_object_t *res = NULL, *ext_obj = NULL;
749
ucl_object_t *url_obj;
750
const char *p, *c, *hash_ptr = NULL;
751
char *url_copy = NULL;
752
unsigned char *url_buf;
753
size_t url_buflen;
754
755
if (ref[0] != '#') {
756
hash_ptr = strrchr (ref, '#');
757
758
if (hash_ptr) {
759
url_copy = malloc (hash_ptr - ref + 1);
760
761
if (url_copy == NULL) {
762
ucl_schema_create_error (err, UCL_SCHEMA_INTERNAL_ERROR, root,
763
"cannot allocate memory");
764
return NULL;
765
}
766
767
ucl_strlcpy (url_copy, ref, hash_ptr - ref + 1);
768
p = url_copy;
769
}
770
else {
771
/* Full URL */
772
p = ref;
773
}
774
775
ext_obj = ucl_object_lookup (ext_ref, p);
776
777
if (ext_obj == NULL) {
778
if (ucl_strnstr (p, "://", strlen (p)) != NULL) {
779
if (!ucl_fetch_url (p, &url_buf, &url_buflen, &url_err, true)) {
780
781
ucl_schema_create_error (err,
782
UCL_SCHEMA_INVALID_SCHEMA,
783
root,
784
"cannot fetch reference %s: %s",
785
p,
786
url_err != NULL ? utstring_body (url_err)
787
: "unknown");
788
free (url_copy);
789
790
return NULL;
791
}
792
}
793
else {
794
if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err,
795
true)) {
796
ucl_schema_create_error (err,
797
UCL_SCHEMA_INVALID_SCHEMA,
798
root,
799
"cannot fetch reference %s: %s",
800
p,
801
url_err != NULL ? utstring_body (url_err)
802
: "unknown");
803
free (url_copy);
804
805
return NULL;
806
}
807
}
808
809
parser = ucl_parser_new (0);
810
811
if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) {
812
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
813
"cannot fetch reference %s: %s", p,
814
ucl_parser_get_error (parser));
815
ucl_parser_free (parser);
816
free (url_copy);
817
818
return NULL;
819
}
820
821
url_obj = ucl_parser_get_object (parser);
822
ext_obj = url_obj;
823
ucl_object_insert_key (ext_ref, url_obj, p, 0, true);
824
free (url_buf);
825
}
826
827
free (url_copy);
828
829
if (hash_ptr) {
830
p = hash_ptr + 1;
831
}
832
else {
833
p = "";
834
}
835
}
836
else {
837
p = ref + 1;
838
}
839
840
res = ext_obj != NULL ? ext_obj : root;
841
*nroot = res;
842
843
if (*p == '/') {
844
p++;
845
}
846
else if (*p == '\0') {
847
return res;
848
}
849
850
c = p;
851
852
while (*p != '\0') {
853
if (*p == '/') {
854
if (p - c == 0) {
855
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
856
"reference %s is invalid, empty path component", ref);
857
return NULL;
858
}
859
/* Now we have some url part, so we need to figure out where we are */
860
res = ucl_schema_resolve_ref_component (res, c, p - c, err);
861
if (res == NULL) {
862
return NULL;
863
}
864
c = p + 1;
865
}
866
p ++;
867
}
868
869
if (p - c != 0) {
870
res = ucl_schema_resolve_ref_component (res, c, p - c, err);
871
}
872
873
if (res == NULL || res->type != UCL_OBJECT) {
874
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
875
"reference %s is invalid, cannot find specified object",
876
ref);
877
return NULL;
878
}
879
880
return res;
881
}
882
883
static bool
884
ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
885
struct ucl_schema_error *err)
886
{
887
const ucl_object_t *elt, *cur;
888
int64_t constraint, i;
889
890
elt = ucl_object_lookup (schema, "maxValues");
891
if (elt != NULL && elt->type == UCL_INT) {
892
constraint = ucl_object_toint (elt);
893
cur = obj;
894
i = 0;
895
while (cur) {
896
if (i > constraint) {
897
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
898
"object has more values than defined: %ld",
899
(long int)constraint);
900
return false;
901
}
902
i ++;
903
cur = cur->next;
904
}
905
}
906
elt = ucl_object_lookup (schema, "minValues");
907
if (elt != NULL && elt->type == UCL_INT) {
908
constraint = ucl_object_toint (elt);
909
cur = obj;
910
i = 0;
911
while (cur) {
912
if (i >= constraint) {
913
break;
914
}
915
i ++;
916
cur = cur->next;
917
}
918
if (i < constraint) {
919
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
920
"object has less values than defined: %ld",
921
(long int)constraint);
922
return false;
923
}
924
}
925
926
return true;
927
}
928
929
static bool
930
ucl_schema_validate (const ucl_object_t *schema,
931
const ucl_object_t *obj, bool try_array,
932
struct ucl_schema_error *err,
933
const ucl_object_t *root,
934
ucl_object_t *external_refs)
935
{
936
const ucl_object_t *elt, *cur, *ref_root;
937
ucl_object_iter_t iter = NULL;
938
bool ret;
939
940
if (schema->type != UCL_OBJECT) {
941
ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
942
"schema is %s instead of object",
943
ucl_object_type_to_string (schema->type));
944
return false;
945
}
946
947
if (try_array) {
948
/*
949
* Special case for multiple values
950
*/
951
if (!ucl_schema_validate_values (schema, obj, err)) {
952
return false;
953
}
954
LL_FOREACH (obj, cur) {
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
1069
ucl_object_validate (const ucl_object_t *schema,
1070
const ucl_object_t *obj, struct ucl_schema_error *err)
1071
{
1072
return ucl_object_validate_root_ext (schema, obj, schema, NULL, err);
1073
}
1074
1075
bool
1076
ucl_object_validate_root (const ucl_object_t *schema,
1077
const ucl_object_t *obj,
1078
const ucl_object_t *root,
1079
struct ucl_schema_error *err)
1080
{
1081
return ucl_object_validate_root_ext (schema, obj, root, NULL, err);
1082
}
1083
1084
bool
1085
ucl_object_validate_root_ext (const ucl_object_t *schema,
1086
const ucl_object_t *obj,
1087
const ucl_object_t *root,
1088
ucl_object_t *ext_refs,
1089
struct ucl_schema_error *err)
1090
{
1091
bool ret, need_unref = false;
1092
1093
if (ext_refs == NULL) {
1094
ext_refs = ucl_object_typed_new (UCL_OBJECT);
1095
need_unref = true;
1096
}
1097
1098
ret = ucl_schema_validate (schema, obj, true, err, root, ext_refs);
1099
1100
if (need_unref) {
1101
ucl_object_unref (ext_refs);
1102
}
1103
1104
return ret;
1105
}
1106
1107