Line data Source code
1 : /*-
2 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
3 : * All rights reserved.
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice, this list of conditions and the following disclaimer
10 : * in this position and unchanged.
11 : * 2. Redistributions in binary form must reproduce the above copyright
12 : * notice, this list of conditions and the following disclaimer in the
13 : * documentation and/or other materials provided with the distribution.
14 : *
15 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 : */
26 :
27 : #include <ucl.h>
28 :
29 : #include "pkg.h"
30 : #include "private/event.h"
31 : #include "private/pkg.h"
32 :
33 : static ucl_object_t *repo_meta_schema_v1 = NULL;
34 :
35 : static void
36 276 : pkg_repo_meta_set_default(struct pkg_repo_meta *meta)
37 : {
38 276 : meta->digest_format = PKG_HASH_TYPE_SHA256_BASE32;
39 276 : meta->packing_format = TXZ;
40 :
41 : /* Not use conflicts for now */
42 276 : meta->conflicts = NULL;
43 276 : meta->conflicts_archive = NULL;
44 276 : meta->manifests = strdup("packagesite.yaml");
45 276 : meta->manifests_archive = strdup("packagesite");
46 276 : meta->digests = strdup("digests");
47 276 : meta->digests_archive = strdup("digests");
48 276 : meta->filesite = strdup("filesite.yaml");
49 276 : meta->filesite_archive = strdup("filesite");
50 : /* Not using fulldb */
51 276 : meta->fulldb = NULL;
52 276 : meta->fulldb_archive = NULL;
53 276 : meta->version = 1;
54 276 : }
55 :
56 : void
57 29 : pkg_repo_meta_free(struct pkg_repo_meta *meta)
58 : {
59 : struct pkg_repo_meta_key *k, *ktmp;
60 :
61 : /*
62 : * It is safe to free NULL pointer by standard
63 : */
64 29 : if (meta != NULL) {
65 29 : free(meta->conflicts);
66 29 : free(meta->manifests);
67 29 : free(meta->digests);
68 29 : free(meta->fulldb);
69 29 : free(meta->filesite);
70 29 : free(meta->conflicts_archive);
71 29 : free(meta->manifests_archive);
72 29 : free(meta->digests_archive);
73 29 : free(meta->fulldb_archive);
74 29 : free(meta->filesite_archive);
75 29 : free(meta->maintainer);
76 29 : free(meta->source);
77 29 : free(meta->source_identifier);
78 29 : HASH_ITER(hh, meta->keys, k, ktmp) {
79 0 : HASH_DELETE(hh, meta->keys, k);
80 0 : free(k->name);
81 0 : free(k->pubkey);
82 0 : free(k->pubkey_type);
83 0 : free(k);
84 : }
85 29 : free(meta);
86 : }
87 29 : }
88 :
89 : static ucl_object_t*
90 34 : pkg_repo_meta_open_schema_v1()
91 : {
92 : struct ucl_parser *parser;
93 : static const char meta_schema_str_v1[] = ""
94 : "{"
95 : "type = object;"
96 : "properties {"
97 : "version = {type = integer};\n"
98 : "maintainer = {type = string};\n"
99 : "source = {type = string};\n"
100 : "packing_format = {enum = [txz, tbz, tgz, tar]};\n"
101 : "digest_format = {enum = [sha256_base32, sha256_hex, blake2_base32]};\n"
102 : "digests = {type = string};\n"
103 : "manifests = {type = string};\n"
104 : "conflicts = {type = string};\n"
105 : "fulldb = {type = string};\n"
106 : "filesite = {type = string};\n"
107 : "digests_archive = {type = string};\n"
108 : "manifests_archive = {type = string};\n"
109 : "conflicts_archive = {type = string};\n"
110 : "fulldb_archive = {type = string};\n"
111 : "filesite_archive = {type = string};\n"
112 : "source_identifier = {type = string};\n"
113 : "revision = {type = integer};\n"
114 : "eol = {type = integer};\n"
115 : "cert = {"
116 : " type = object;\n"
117 : " properties {"
118 : " type = {enum = [rsa]};\n"
119 : " data = {type = string};\n"
120 : " name = {type = string};\n"
121 : " }"
122 : " required = [type, data, name];\n"
123 : "};\n"
124 :
125 : "}\n"
126 : "required = [version]\n"
127 : "}";
128 :
129 34 : if (repo_meta_schema_v1 != NULL)
130 15 : return (repo_meta_schema_v1);
131 :
132 19 : parser = ucl_parser_new(0);
133 19 : if (!ucl_parser_add_chunk(parser, meta_schema_str_v1,
134 : sizeof(meta_schema_str_v1) - 1)) {
135 0 : pkg_emit_error("cannot parse schema for repo meta: %s",
136 : ucl_parser_get_error(parser));
137 0 : ucl_parser_free(parser);
138 0 : return (NULL);
139 : }
140 :
141 19 : repo_meta_schema_v1 = ucl_parser_get_object(parser);
142 19 : ucl_parser_free(parser);
143 :
144 19 : return (repo_meta_schema_v1);
145 : }
146 :
147 : static struct pkg_repo_meta_key*
148 0 : pkg_repo_meta_parse_cert(const ucl_object_t *obj)
149 : {
150 : struct pkg_repo_meta_key *key;
151 :
152 0 : key = calloc(1, sizeof(*key));
153 0 : if (key == NULL) {
154 0 : pkg_emit_errno("pkg_repo_meta_parse", "malloc failed for pkg_repo_meta_key");
155 0 : return (NULL);
156 : }
157 :
158 : /*
159 : * It is already validated so just use it as is
160 : */
161 0 : key->name = strdup(ucl_object_tostring(ucl_object_find_key(obj, "name")));
162 0 : key->pubkey = strdup(ucl_object_tostring(ucl_object_find_key(obj, "data")));
163 0 : key->pubkey_type = strdup(ucl_object_tostring(ucl_object_find_key(obj, "type")));
164 :
165 0 : return (key);
166 : }
167 :
168 : #define META_EXTRACT_STRING(field) do { \
169 : obj = ucl_object_find_key(top, (#field)); \
170 : if (obj != NULL && obj->type == UCL_STRING) { \
171 : free(meta->field); \
172 : meta->field = strdup(ucl_object_tostring(obj)); \
173 : } \
174 : } while (0)
175 :
176 : static int
177 34 : pkg_repo_meta_parse(ucl_object_t *top, struct pkg_repo_meta **target, int version)
178 : {
179 : const ucl_object_t *obj, *cur;
180 34 : ucl_object_iter_t iter = NULL;
181 : struct pkg_repo_meta *meta;
182 : struct pkg_repo_meta_key *cert;
183 :
184 34 : meta = calloc(1, sizeof(*meta));
185 34 : if (meta == NULL) {
186 0 : pkg_emit_errno("pkg_repo_meta_parse", "malloc failed for pkg_repo_meta");
187 0 : return (EPKG_FATAL);
188 : }
189 :
190 34 : pkg_repo_meta_set_default(meta);
191 34 : meta->version = version;
192 :
193 34 : META_EXTRACT_STRING(maintainer);
194 34 : META_EXTRACT_STRING(source);
195 :
196 34 : META_EXTRACT_STRING(conflicts);
197 34 : META_EXTRACT_STRING(digests);
198 34 : META_EXTRACT_STRING(manifests);
199 34 : META_EXTRACT_STRING(fulldb);
200 34 : META_EXTRACT_STRING(filesite);
201 34 : META_EXTRACT_STRING(conflicts_archive);
202 34 : META_EXTRACT_STRING(digests_archive);
203 34 : META_EXTRACT_STRING(manifests_archive);
204 34 : META_EXTRACT_STRING(fulldb_archive);
205 34 : META_EXTRACT_STRING(filesite_archive);
206 :
207 34 : META_EXTRACT_STRING(source_identifier);
208 :
209 34 : obj = ucl_object_find_key(top, "eol");
210 34 : if (obj != NULL && obj->type == UCL_INT) {
211 0 : meta->eol = ucl_object_toint(obj);
212 : }
213 :
214 34 : obj = ucl_object_find_key(top, "revision");
215 34 : if (obj != NULL && obj->type == UCL_INT) {
216 0 : meta->revision = ucl_object_toint(obj);
217 : }
218 :
219 34 : obj = ucl_object_find_key(top, "packing_format");
220 34 : if (obj != NULL && obj->type == UCL_STRING) {
221 34 : meta->packing_format = packing_format_from_string(ucl_object_tostring(obj));
222 : }
223 :
224 34 : obj = ucl_object_find_key(top, "digest_format");
225 34 : if (obj != NULL && obj->type == UCL_STRING) {
226 34 : meta->digest_format = pkg_checksum_type_from_string(ucl_object_tostring(obj));
227 : }
228 :
229 34 : obj = ucl_object_find_key(top, "cert");
230 68 : while ((cur = ucl_iterate_object(obj, &iter, false)) != NULL) {
231 0 : cert = pkg_repo_meta_parse_cert(cur);
232 0 : if (cert != NULL)
233 0 : HASH_ADD_STR(meta->keys, name, cert);
234 : }
235 :
236 34 : *target = meta;
237 :
238 34 : return (EPKG_OK);
239 : }
240 :
241 : #undef META_EXTRACT_STRING
242 :
243 : static int
244 34 : pkg_repo_meta_version(ucl_object_t *top)
245 : {
246 : const ucl_object_t *obj;
247 :
248 34 : if ((obj = ucl_object_find_key(top, "version")) != NULL) {
249 34 : if (obj->type == UCL_INT) {
250 34 : return (ucl_object_toint(obj));
251 : }
252 : }
253 :
254 0 : return (-1);
255 : }
256 :
257 : int
258 34 : pkg_repo_meta_load(const char *file, struct pkg_repo_meta **target)
259 : {
260 : struct ucl_parser *parser;
261 : ucl_object_t *top, *schema;
262 : struct ucl_schema_error err;
263 : int version;
264 :
265 34 : parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);
266 :
267 34 : if (!ucl_parser_add_file(parser, file)) {
268 0 : pkg_emit_error("cannot parse repository meta from %s: %s", file,
269 : ucl_parser_get_error(parser));
270 0 : ucl_parser_free(parser);
271 0 : return (EPKG_FATAL);
272 : }
273 :
274 34 : top = ucl_parser_get_object(parser);
275 34 : ucl_parser_free(parser);
276 :
277 34 : version = pkg_repo_meta_version(top);
278 34 : if (version == -1) {
279 0 : pkg_emit_error("repository meta %s has wrong version or wrong format", file);
280 0 : ucl_object_unref(top);
281 0 : return (EPKG_FATAL);
282 : }
283 :
284 : /* Now we support only v1 meta */
285 34 : if (version == 1) {
286 34 : schema = pkg_repo_meta_open_schema_v1();
287 :
288 34 : if (schema != NULL) {
289 34 : if (!ucl_object_validate(schema, top, &err)) {
290 0 : pkg_emit_error("repository meta %s cannot be validated: %s", file, err.msg);
291 0 : ucl_object_unref(top);
292 0 : return (EPKG_FATAL);
293 : }
294 : }
295 : }
296 : else {
297 0 : pkg_emit_error("repository meta %s has wrong version %d", file, version);
298 0 : ucl_object_unref(top);
299 0 : return (EPKG_FATAL);
300 : }
301 :
302 34 : return (pkg_repo_meta_parse(top, target, version));
303 : }
304 :
305 : struct pkg_repo_meta *
306 242 : pkg_repo_meta_default(void)
307 : {
308 : struct pkg_repo_meta *meta;
309 :
310 242 : meta = calloc(1, sizeof(*meta));
311 242 : if (meta == NULL) {
312 0 : pkg_emit_errno("pkg_repo_meta_default", "malloc failed for pkg_repo_meta");
313 0 : return (NULL);
314 : }
315 :
316 242 : pkg_repo_meta_set_default(meta);
317 :
318 242 : return (meta);
319 : }
320 :
321 : #define META_EXPORT_FIELD(result, meta, field, type) do { \
322 : if (meta->field != 0) \
323 : ucl_object_insert_key((result), ucl_object_from ## type (meta->field), \
324 : #field, 0, false); \
325 : } while(0)
326 :
327 : #define META_EXPORT_FIELD_FUNC(result, meta, field, type, func) do { \
328 : if (func(meta->field) != 0) \
329 : ucl_object_insert_key((result), ucl_object_from ## type (func(meta->field)), \
330 : #field, 0, false); \
331 : } while(0)
332 :
333 :
334 : ucl_object_t *
335 11 : pkg_repo_meta_to_ucl(struct pkg_repo_meta *meta)
336 : {
337 11 : ucl_object_t *result = ucl_object_typed_new(UCL_OBJECT);
338 :
339 11 : META_EXPORT_FIELD(result, meta, version, int);
340 11 : META_EXPORT_FIELD(result, meta, maintainer, string);
341 11 : META_EXPORT_FIELD(result, meta, source, string);
342 :
343 11 : META_EXPORT_FIELD_FUNC(result, meta, packing_format, string,
344 : packing_format_to_string);
345 11 : META_EXPORT_FIELD_FUNC(result, meta, digest_format, string,
346 : pkg_checksum_type_to_string);
347 :
348 11 : META_EXPORT_FIELD(result, meta, digests, string);
349 11 : META_EXPORT_FIELD(result, meta, manifests, string);
350 11 : META_EXPORT_FIELD(result, meta, conflicts, string);
351 11 : META_EXPORT_FIELD(result, meta, fulldb, string);
352 11 : META_EXPORT_FIELD(result, meta, filesite, string);
353 11 : META_EXPORT_FIELD(result, meta, digests_archive, string);
354 11 : META_EXPORT_FIELD(result, meta, manifests_archive, string);
355 11 : META_EXPORT_FIELD(result, meta, conflicts_archive, string);
356 11 : META_EXPORT_FIELD(result, meta, fulldb_archive, string);
357 11 : META_EXPORT_FIELD(result, meta, filesite_archive, string);
358 :
359 11 : META_EXPORT_FIELD(result, meta, source_identifier, string);
360 11 : META_EXPORT_FIELD(result, meta, revision, int);
361 11 : META_EXPORT_FIELD(result, meta, eol, int);
362 :
363 : /* TODO: export keys */
364 :
365 11 : return (result);
366 : }
367 :
368 : #undef META_EXPORT_FIELD
369 : #undef META_EXPORT_FIELD_FUNC
370 :
371 : #define META_SPECIAL_FILE(file, meta, field) \
372 : special || (meta->field == NULL ? false : (strcmp(file, meta->field) == 0))
373 :
374 : bool
375 103 : pkg_repo_meta_is_special_file(const char *file, struct pkg_repo_meta *meta)
376 : {
377 103 : bool special = false;
378 :
379 103 : special = META_SPECIAL_FILE(file, meta, digests_archive);
380 103 : special = META_SPECIAL_FILE(file, meta, manifests_archive);
381 103 : special = META_SPECIAL_FILE(file, meta, filesite_archive);
382 103 : special = META_SPECIAL_FILE(file, meta, conflicts_archive);
383 103 : special = META_SPECIAL_FILE(file, meta, fulldb_archive);
384 :
385 103 : return (special);
386 : }
|