2 * Copyright (c) 2019, SUSE LLC.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
17 #include "solv_jsonparser.h"
19 #include "repo_conda.h"
20 #include "solv_xfopen.h"
45 struct sigdata *sigdata;
53 parse_deps(struct parsedata *pd, struct solv_jsonparser *jp, Offset *depp)
56 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
58 if (type == JP_STRING)
60 Id id = pool_conda_matchspec(pd->pool, jp->value);
62 *depp = repo_addid_dep(pd->repo, *depp, id, 0);
65 type = jsonparser_skip(jp, type);
71 parse_otherdeps(struct parsedata *pd, struct solv_jsonparser *jp, Id handle, Id keyname)
74 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
76 if (type == JP_STRING)
78 Id id = pool_conda_matchspec(pd->pool, jp->value);
80 repodata_add_idarray(pd->data, handle, keyname, id);
83 type = jsonparser_skip(jp, type);
89 parse_trackfeatures_array(struct parsedata *pd, struct solv_jsonparser *jp, Id handle)
92 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
94 if (type == JP_STRING)
96 char *p = jp->value, *pe;
97 while (*p == ' ' || *p == '\t')
101 for (pe = p + strlen(p) - 1; pe > p; pe--)
102 if (*pe != ' ' && *pe != '\t')
104 repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p + 1, 1));
107 type = jsonparser_skip(jp, type);
113 parse_trackfeatures_string(struct parsedata *pd, const char *p, Id handle)
118 if (*p == ' ' || *p == '\t' || *p == ',')
121 while (*pe && *pe != ' ' && *pe != '\t' && *pe != ',')
123 repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p, 1));
129 swap_solvables(Pool *pool, Repodata *data, Id pa, Id pb)
133 tmp = pool->solvables[pa];
134 pool->solvables[pa] = pool->solvables[pb];
135 pool->solvables[pb] = tmp;
136 repodata_swap_attrs(data, pa, pb);
140 fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
142 size_t l = strlen(fn), extl = 0;
144 if (l > 6 && !strcmp(fn + l - 6, ".conda"))
146 else if (l > 8 && !strcmp(fn + l - 8, ".tar.bz2"))
150 fnid = stringpool_strn2id(&pd->fnpool, fn, l - extl, create);
153 if (fnid * 2 + 2 > pd->fndata.count)
154 queue_insertn(&pd->fndata, pd->fndata.count, fnid * 2 + 2 - pd->fndata.count, 0);
156 *fntypep = extl == 8 ? 1 : 2; /* 1: legacy .tar.bz2 2: .conda */
157 return pd->fndata.elements + 2 * fnid;
161 fn2sigdata(struct parsedata *pd, const char *fn, int create)
163 Id id = stringpool_str2id(&pd->sigpool, fn, create);
166 if (id >= pd->nsigdata)
168 int n = id - pd->nsigdata + 1;
169 pd->sigdata = solv_realloc2(pd->sigdata, pd->nsigdata + n, sizeof(struct sigdata));
170 memset(pd->sigdata + pd->nsigdata, 0, n * sizeof(struct sigdata));
173 return pd->sigdata + id;
177 freesigdata(struct parsedata *pd)
180 for (i = 0; i < pd->nsigdata; i++)
181 solv_free(pd->sigdata[i].sigs);
182 pd->sigdata = solv_free(pd->sigdata);
187 set_xdata(struct parsedata *pd, int handle, char *fn, char *pkgjson, int delayedlocation)
190 handle -= pd->repo->start;
191 if (handle >= pd->nxdata)
194 if (!fn && !pkgjson && !delayedlocation)
196 n = handle - pd->nxdata + 16;
197 pd->xdata = solv_realloc2(pd->xdata, pd->nxdata + n, sizeof(struct xdata));
198 memset(pd->xdata + pd->nxdata, 0, n * sizeof(struct xdata));
201 xd = pd->xdata + handle;
205 solv_free(xd->pkgjson);
207 xd->pkgjson = pkgjson;
208 xd->delayedlocation = delayedlocation;
212 move_xdata(struct parsedata *pd, int fromhandle, int tohandle)
214 char *fn = 0, *pkgjson = 0;
215 int delayedlocation = 0;
216 fromhandle -= pd->repo->start;
217 if (fromhandle < pd->nxdata)
219 struct xdata *xd = pd->xdata + fromhandle;
221 pkgjson = xd->pkgjson;
222 delayedlocation = xd->delayedlocation;
225 xd->delayedlocation = 0;
227 set_xdata(pd, tohandle, fn, pkgjson, delayedlocation);
230 static int parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson);
233 parse_package_with_pkgjson(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
237 char *pkgjson = NULL;
240 type = jsonparser_collect(jp, JP_OBJECT, &pkgjson);
241 if (type == JP_OBJECT_END && (fp = solv_fmemopen(pkgjson, strlen(pkgjson), "r")) != 0)
243 struct solv_jsonparser jp2;
244 jsonparser_init(&jp2, fp);
246 type = jsonparser_parse(&jp2);
247 type = type == JP_OBJECT ? parse_package(pd, &jp2, kfn, pkgjson) : JP_ERROR;
248 jsonparser_free(&jp2);
256 parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson)
258 int type = JP_OBJECT;
259 Pool *pool= pd->pool;
260 Repodata *data = pd->data;
265 Id *fndata = 0, fntype = 0;
267 if (!pkgjson && (pd->flags & CONDA_ADD_WITH_SIGNATUREDATA) != 0)
268 return parse_package_with_pkgjson(pd, jp, kfn);
270 handle = repo_add_solvable(pd->repo);
271 s = pool_id2solvable(pool, handle);
272 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
274 if (type == JP_STRING && !strcmp(jp->key, "build"))
275 repodata_add_poolstr_array(data, handle, SOLVABLE_BUILDFLAVOR, jp->value);
276 else if (type == JP_NUMBER && !strcmp(jp->key, "build_number"))
277 repodata_set_str(data, handle, SOLVABLE_BUILDVERSION, jp->value);
278 else if (type == JP_ARRAY && !strcmp(jp->key, "depends"))
279 type = parse_deps(pd, jp, &s->requires);
280 else if (type == JP_ARRAY && !strcmp(jp->key, "requires"))
281 type = parse_deps(pd, jp, &s->requires);
282 else if (type == JP_ARRAY && !strcmp(jp->key, "constrains"))
283 type = parse_otherdeps(pd, jp, handle, SOLVABLE_CONSTRAINS);
284 else if (type == JP_STRING && !strcmp(jp->key, "license"))
285 repodata_add_poolstr_array(data, handle, SOLVABLE_LICENSE, jp->value);
286 else if (type == JP_STRING && !strcmp(jp->key, "md5"))
287 repodata_set_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, jp->value);
288 else if (type == JP_STRING && !strcmp(jp->key, "sha256"))
289 repodata_set_checksum(data, handle, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, jp->value);
290 else if (type == JP_STRING && !strcmp(jp->key, "name"))
291 s->name = pool_str2id(pool, jp->value, 1);
292 else if (type == JP_STRING && !strcmp(jp->key, "version"))
293 s->evr= pool_str2id(pool, jp->value, 1);
294 else if (type == JP_STRING && !strcmp(jp->key, "fn") && !fn)
295 fn = solv_strdup(jp->value);
296 else if (type == JP_STRING && !strcmp(jp->key, "subdir") && !subdir)
297 subdir = solv_strdup(jp->value);
298 else if (type == JP_NUMBER && !strcmp(jp->key, "size"))
299 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(jp->value, 0, 10));
300 else if (type == JP_NUMBER && !strcmp(jp->key, "timestamp"))
302 unsigned long long ts = strtoull(jp->value, 0, 10);
303 if (ts > 253402300799ULL)
305 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
307 else if (type == JP_STRING && !strcmp(jp->key, "track_features"))
308 parse_trackfeatures_string(pd, jp->value, handle);
309 else if (type == JP_ARRAY && !strcmp(jp->key, "track_features"))
310 type = parse_trackfeatures_array(pd, jp, handle);
312 type = jsonparser_skip(jp, type);
314 /* if we have a global subdir make sure that it matches */
315 if (subdir && pd->subdir && strcmp(subdir, pd->subdir) != 0)
317 /* we used to return an error here, but classic conda
318 * just overwrites the package subdir with the global
321 pd->error = "subdir mismatch";
325 subdir = solv_strdup(pd->subdir);
331 int delayedlocation = (subdir || pd->subdir) ? 0 : 1;
332 if (pkgjson || delayedlocation)
333 set_xdata(pd, handle, solv_strdup(fn ? fn : kfn), pkgjson ? solv_strdup(pkgjson) : 0, delayedlocation);
334 if (!delayedlocation)
335 repodata_set_location(data, handle, 0, subdir ? subdir : pd->subdir, fn ? fn : kfn);
336 fndata = fn2data(pd, fn ? fn : kfn, &fntype, 1);
343 s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
347 /* deal with legacy package entries */
348 if (fndata[0] && fndata[0] > fntype)
350 /* ignore this package */
351 repo_free_solvable(pd->repo, handle, 1);
352 set_xdata(pd, handle, 0, 0, 0);
355 if (fndata[0] && fndata[0] < fntype)
357 /* replace old package */
358 swap_solvables(pool, data, handle, fndata[1]);
359 move_xdata(pd, handle, fndata[1]);
360 repo_free_solvable(pd->repo, handle, 1);
370 parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
372 int type = JP_OBJECT;
373 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
375 if (type == JP_OBJECT)
377 char *fn = solv_strdup(jp->key);
378 type = parse_package(pd, jp, fn, 0);
382 type = jsonparser_skip(jp, type);
388 parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
391 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
393 if (type == JP_OBJECT)
394 type = parse_package(pd, jp, 0, 0);
396 type = jsonparser_skip(jp, type);
402 parse_info(struct parsedata *pd, struct solv_jsonparser *jp)
404 int type = JP_OBJECT;
405 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
407 if (type == JP_STRING && !strcmp(jp->key, "subdir"))
410 pd->subdir = strdup(jp->value);
411 else if (strcmp(pd->subdir, jp->value))
413 pd->error = "subdir mismatch";
422 parse_signatures(struct parsedata *pd, struct solv_jsonparser *jp)
424 int type = JP_OBJECT;
425 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
428 if (type != JP_OBJECT)
430 type = jsonparser_skip(jp, type);
433 sd = fn2sigdata(pd, jp->key, 1);
434 sd->sigs = solv_free(sd->sigs);
435 type = jsonparser_collect(jp, type, &sd->sigs);
441 parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
443 int type = JP_OBJECT;
444 while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
446 if (type == JP_OBJECT && !strcmp("info", jp->key))
447 type = parse_info(pd, jp);
448 if (type == JP_OBJECT && !strcmp("packages", jp->key))
449 type = parse_packages(pd, jp);
450 else if (type == JP_ARRAY && !strcmp("packages", jp->key))
451 type = parse_packages2(pd, jp);
452 else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
453 type = parse_packages(pd, jp);
454 else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
455 type = parse_packages2(pd, jp);
456 if (type == JP_OBJECT && !strcmp("signatures", jp->key))
457 type = parse_signatures(pd, jp);
459 type = jsonparser_skip(jp, type);
465 repo_add_conda(Repo *repo, FILE *fp, int flags)
467 Pool *pool = repo->pool;
468 struct solv_jsonparser jp;
473 data = repo_add_repodata(repo, flags);
475 memset(&pd, 0, sizeof(pd));
480 stringpool_init_empty(&pd.fnpool);
481 stringpool_init_empty(&pd.sigpool);
482 queue_init(&pd.fndata);
484 jsonparser_init(&jp, fp);
485 if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
486 ret = pool_error(pool, -1, "repository does not start with an object");
487 else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
490 ret = pool_error(pool, -1, "parse error line %d: %s", jp.line, pd.error);
492 ret = pool_error(pool, -1, "parse error line %d", jp.line);
494 jsonparser_free(&jp);
496 /* finalize parsed packages */
500 struct xdata *xd = pd.xdata;
501 for (i = 0; i < pd.nxdata; i++, xd++)
505 if (xd->delayedlocation)
506 repodata_set_location(data, repo->start + i, 0, pd.subdir, xd->fn);
507 if (xd->pkgjson && pd.nsigdata)
509 struct sigdata *sd = fn2sigdata(&pd, xd->fn, 0);
512 char *s = pool_tmpjoin(pool, "{\"info\":", xd->pkgjson, ",\"signatures\":");
513 s = pool_tmpappend(pool, s, sd->sigs, "}");
514 repodata_set_str(data, repo->start + i, SOLVABLE_SIGNATUREDATA, s);
518 solv_free(xd->pkgjson);
525 stringpool_free(&pd.sigpool);
527 queue_free(&pd.fndata);
528 stringpool_free(&pd.fnpool);
529 solv_free(pd.subdir);
530 if (!(flags & REPO_NO_INTERNALIZE))
531 repodata_internalize(data);