Imported Upstream version 0.7.27
[platform/upstream/libsolv.git] / ext / repo_conda.c
1 /*
2  * Copyright (c) 2019, SUSE LLC.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #define _GNU_SOURCE
9 #include <sys/types.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "pool.h"
15 #include "repo.h"
16 #include "chksum.h"
17 #include "solv_jsonparser.h"
18 #include "conda.h"
19 #include "repo_conda.h"
20 #include "solv_xfopen.h"
21
22 struct sigdata {
23   char *sigs;
24 };
25
26 struct xdata {
27   char *fn;
28   char *pkgjson;
29   int delayedlocation;
30 };
31
32 struct parsedata {
33   Pool *pool;
34   Repo *repo;
35   Repodata *data;
36   int flags;
37
38   char *subdir;
39   char *error;
40
41   Stringpool fnpool;
42   Queue fndata;
43
44   Stringpool sigpool;
45   struct sigdata *sigdata;
46   int nsigdata;
47
48   struct xdata *xdata;
49   int nxdata;
50 };
51
52 static int
53 parse_deps(struct parsedata *pd, struct solv_jsonparser *jp, Offset *depp)
54 {
55   int type = JP_ARRAY;
56   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
57     {
58       if (type == JP_STRING)
59         {
60           Id id = pool_conda_matchspec(pd->pool, jp->value);
61           if (id)
62             *depp = repo_addid_dep(pd->repo, *depp, id, 0);
63         }
64       else
65         type = jsonparser_skip(jp, type);
66     }
67   return type;
68 }
69
70 static int
71 parse_otherdeps(struct parsedata *pd, struct solv_jsonparser *jp, Id handle, Id keyname)
72 {
73   int type = JP_ARRAY;
74   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
75     {
76       if (type == JP_STRING)
77         {
78           Id id = pool_conda_matchspec(pd->pool, jp->value);
79           if (id)
80             repodata_add_idarray(pd->data, handle, keyname, id);
81         }
82       else
83         type = jsonparser_skip(jp, type);
84     }
85   return type;
86 }
87
88 static int
89 parse_trackfeatures_array(struct parsedata *pd, struct solv_jsonparser *jp, Id handle)
90 {
91   int type = JP_ARRAY;
92   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
93     {
94       if (type == JP_STRING)
95         {
96           char *p = jp->value, *pe;
97           while (*p == ' ' || *p == '\t')
98             p++;
99           if (!*p)
100             continue;
101           for (pe = p + strlen(p) - 1; pe > p; pe--)
102             if (*pe != ' ' && *pe != '\t')
103               break;
104           repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p + 1, 1));
105         }
106       else
107         type = jsonparser_skip(jp, type);
108     }
109   return type;
110 }
111
112 static void
113 parse_trackfeatures_string(struct parsedata *pd, const char *p, Id handle)
114 {
115   const char *pe;
116   for (; *p; p++)
117     {
118       if (*p == ' ' || *p == '\t' || *p == ',')
119         continue;
120       pe = p + 1;
121       while (*pe && *pe != ' ' && *pe != '\t' && *pe != ',')
122         pe++;
123       repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p, 1));
124       p = pe - 1;
125     }
126 }
127
128 static void 
129 swap_solvables(Pool *pool, Repodata *data, Id pa, Id pb)
130 {
131   Solvable tmp; 
132
133   tmp = pool->solvables[pa];
134   pool->solvables[pa] = pool->solvables[pb];
135   pool->solvables[pb] = tmp; 
136   repodata_swap_attrs(data, pa, pb); 
137 }
138
139 static Id *
140 fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
141 {
142   size_t l = strlen(fn), extl = 0;
143   Id fnid;
144   if (l > 6 && !strcmp(fn + l - 6, ".conda"))
145     extl = 6;
146   else if (l > 8 && !strcmp(fn + l - 8, ".tar.bz2"))
147     extl = 8;
148   else
149     return 0;
150   fnid = stringpool_strn2id(&pd->fnpool, fn, l - extl, create);
151   if (!fnid)
152     return 0;
153   if (fnid * 2 + 2 > pd->fndata.count)
154     queue_insertn(&pd->fndata, pd->fndata.count, fnid * 2 + 2 - pd->fndata.count, 0);
155   if (fntypep)
156     *fntypep = extl == 8 ? 1 : 2;       /* 1: legacy .tar.bz2  2: .conda */
157   return pd->fndata.elements + 2 * fnid;
158 }
159
160 struct sigdata *
161 fn2sigdata(struct parsedata *pd, const char *fn, int create)
162 {
163   Id id = stringpool_str2id(&pd->sigpool, fn, create);
164   if (!id && !create)
165     return 0;
166   if (id >= pd->nsigdata)
167     {
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));
171       pd->nsigdata += n;
172     }
173   return pd->sigdata + id;
174 }
175
176 void
177 freesigdata(struct parsedata *pd)
178 {
179   int i;
180   for (i = 0; i < pd->nsigdata; i++)
181     solv_free(pd->sigdata[i].sigs);
182   pd->sigdata = solv_free(pd->sigdata);
183   pd->nsigdata = 0;
184 }
185
186 static void
187 set_xdata(struct parsedata *pd, int handle, char *fn, char *pkgjson, int delayedlocation)
188 {
189   struct xdata *xd;
190   handle -= pd->repo->start;
191   if (handle >= pd->nxdata)
192     {
193       int n;
194       if (!fn && !pkgjson && !delayedlocation)
195         return;
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));
199       pd->nxdata += n;
200     }
201   xd = pd->xdata + handle;
202   if (xd->fn)
203     solv_free(xd->fn);
204   if (xd->pkgjson)
205     solv_free(xd->pkgjson);
206   xd->fn = fn;
207   xd->pkgjson = pkgjson;
208   xd->delayedlocation = delayedlocation;
209 }
210
211 static void
212 move_xdata(struct parsedata *pd, int fromhandle, int tohandle)
213 {
214   char *fn = 0, *pkgjson = 0;
215   int delayedlocation = 0;
216   fromhandle -= pd->repo->start;
217   if (fromhandle < pd->nxdata)
218     {
219       struct xdata *xd = pd->xdata + fromhandle;
220       fn = xd->fn;
221       pkgjson = xd->pkgjson;
222       delayedlocation = xd->delayedlocation;
223       xd->fn = 0;
224       xd->pkgjson = 0;
225       xd->delayedlocation = 0;
226     }
227   set_xdata(pd, tohandle, fn, pkgjson, delayedlocation);
228 }
229
230 static int parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson);
231
232 static int
233 parse_package_with_pkgjson(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
234 {
235   FILE *fp;
236   int type;
237   char *pkgjson = NULL;
238   int line = jp->line;
239
240   type = jsonparser_collect(jp, JP_OBJECT, &pkgjson);
241   if (type == JP_OBJECT_END && (fp = solv_fmemopen(pkgjson, strlen(pkgjson), "r")) != 0)
242     {
243       struct solv_jsonparser jp2;
244       jsonparser_init(&jp2, fp);
245       jp2.line = line;
246       type = jsonparser_parse(&jp2);
247       type = type == JP_OBJECT ? parse_package(pd, &jp2, kfn, pkgjson) : JP_ERROR;
248       jsonparser_free(&jp2);
249       fclose(fp);
250     }
251   solv_free(pkgjson);
252   return type;
253 }
254
255 static int
256 parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson)
257 {
258   int type = JP_OBJECT;
259   Pool *pool= pd->pool;
260   Repodata *data = pd->data;
261   Solvable *s;
262   Id handle;
263   char *fn = 0;
264   char *subdir = 0;
265   Id *fndata = 0, fntype = 0;
266
267   if (!pkgjson && (pd->flags & CONDA_ADD_WITH_SIGNATUREDATA) != 0)
268     return parse_package_with_pkgjson(pd, jp, kfn);
269
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)
273     {
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"))
301         {
302           unsigned long long ts = strtoull(jp->value, 0, 10);
303           if (ts > 253402300799ULL)
304             ts /= 1000;
305           repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
306         }
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);
311       else
312         type = jsonparser_skip(jp, type);
313     }
314   /* if we have a global subdir make sure that it matches */
315   if (subdir && pd->subdir && strcmp(subdir, pd->subdir) != 0)
316     {
317       /* we used to return an error here, but classic conda
318        * just overwrites the package subdir with the global
319        * subdir */
320 #if 0
321       pd->error = "subdir mismatch";
322       return JP_ERROR;
323 #else
324       solv_free(subdir);
325       subdir = solv_strdup(pd->subdir);
326 #endif
327     }
328
329   if (fn || kfn)
330     {
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);
337     }
338   solv_free(fn);
339   solv_free(subdir);
340   if (!s->evr)
341     s->evr = 1;
342   if (s->name)
343     s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
344
345   if (fndata)
346     {
347       /* deal with legacy package entries */
348       if (fndata[0] && fndata[0] > fntype)
349         {
350           /* ignore this package */
351           repo_free_solvable(pd->repo, handle, 1);
352           set_xdata(pd, handle, 0, 0, 0);
353           return type;
354         }
355       if (fndata[0] && fndata[0] < fntype)
356         {
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);
361           handle = fndata[1];
362         }
363       fndata[0] = fntype;
364       fndata[1] = handle;
365     }
366   return type;
367 }
368
369 static int
370 parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
371 {
372   int type = JP_OBJECT;
373   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
374     {
375       if (type == JP_OBJECT)
376         {
377           char *fn = solv_strdup(jp->key);
378           type = parse_package(pd, jp, fn, 0);
379           solv_free(fn);
380         }
381       else
382         type = jsonparser_skip(jp, type);
383     }
384   return type;
385 }
386
387 static int
388 parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
389 {
390   int type = JP_ARRAY;
391   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
392     {
393       if (type == JP_OBJECT)
394         type = parse_package(pd, jp, 0, 0);
395       else
396         type = jsonparser_skip(jp, type);
397     }
398   return type;
399 }
400
401 static int
402 parse_info(struct parsedata *pd, struct solv_jsonparser *jp)
403 {
404   int type = JP_OBJECT;
405   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
406     {
407       if (type == JP_STRING && !strcmp(jp->key, "subdir"))
408         {
409           if (!pd->subdir)
410             pd->subdir = strdup(jp->value);
411           else if (strcmp(pd->subdir, jp->value))
412             {
413               pd->error = "subdir mismatch";
414               return JP_ERROR;
415             }
416         }
417     }
418   return type;
419 }
420
421 static int
422 parse_signatures(struct parsedata *pd, struct solv_jsonparser *jp)
423 {
424   int type = JP_OBJECT;
425   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
426     {
427       struct sigdata *sd;
428       if (type != JP_OBJECT)
429         {
430           type = jsonparser_skip(jp, type);
431           continue;
432         }
433       sd = fn2sigdata(pd, jp->key, 1);
434       sd->sigs = solv_free(sd->sigs);
435       type = jsonparser_collect(jp, type, &sd->sigs);
436     }
437   return type;
438 }
439
440 static int
441 parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
442 {
443   int type = JP_OBJECT;
444   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
445     {
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);
458       else
459         type = jsonparser_skip(jp, type);
460     }
461   return type;
462 }
463
464 int
465 repo_add_conda(Repo *repo, FILE *fp, int flags)
466 {
467   Pool *pool = repo->pool;
468   struct solv_jsonparser jp;
469   struct parsedata pd;
470   Repodata *data;
471   int type, ret = 0;
472
473   data = repo_add_repodata(repo, flags);
474
475   memset(&pd, 0, sizeof(pd));
476   pd.pool = pool;
477   pd.repo = repo;
478   pd.data = data;
479   pd.flags = flags;
480   stringpool_init_empty(&pd.fnpool);
481   stringpool_init_empty(&pd.sigpool);
482   queue_init(&pd.fndata);
483
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)
488     {
489       if (pd.error)
490         ret = pool_error(pool, -1, "parse error line %d: %s", jp.line, pd.error);
491       else
492         ret = pool_error(pool, -1, "parse error line %d", jp.line);
493     }
494   jsonparser_free(&jp);
495
496   /* finalize parsed packages */
497   if (pd.xdata)
498     {
499       int i;
500       struct xdata *xd = pd.xdata;
501       for (i = 0; i < pd.nxdata; i++, xd++)
502         {
503           if (!xd->fn)
504             continue;
505           if (xd->delayedlocation)
506             repodata_set_location(data, repo->start + i, 0, pd.subdir, xd->fn);
507           if (xd->pkgjson && pd.nsigdata)
508             {
509               struct sigdata *sd = fn2sigdata(&pd, xd->fn, 0);
510               if (sd && sd->sigs)
511                 {
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);
515                 }
516             }
517           solv_free(xd->fn);
518           solv_free(xd->pkgjson);
519         }
520       solv_free(pd.xdata);
521     }
522
523   if (pd.sigdata)
524     freesigdata(&pd);
525   stringpool_free(&pd.sigpool);
526
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);
532
533   return ret;
534 }
535