Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

ucl_schema.c 28KB

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