ucl 0.1.4 → 0.2.0

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