You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ucl_schema.c 28KB

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