- make old rpmdb pubkey support optional
[platform/upstream/libsolv.git] / ext / repo_deb.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <zlib.h>
15
16 #include "pool.h"
17 #include "repo.h"
18 #include "util.h"
19 #include "chksum.h"
20 #include "repo_deb.h"
21
22 static unsigned char *
23 decompress(unsigned char *in, int inl, int *outlp)
24 {
25   z_stream strm;
26   int outl, ret;
27   unsigned char *out;
28
29   memset(&strm, 0, sizeof(strm));
30   strm.next_in = in;
31   strm.avail_in = inl;
32   out = solv_malloc(4096);
33   strm.next_out = out;
34   strm.avail_out = 4096;
35   outl = 0;
36   ret = inflateInit2(&strm, -MAX_WBITS);
37   if (ret != Z_OK)
38     {
39       free(out);
40       return 0;
41     }
42   for (;;)
43     {
44       if (strm.avail_out == 0)
45         {
46           outl += 4096;
47           out = solv_realloc(out, outl + 4096);
48           strm.next_out = out + outl;
49           strm.avail_out = 4096;
50         }
51       ret = inflate(&strm, Z_NO_FLUSH);
52       if (ret == Z_STREAM_END)
53         break;
54       if (ret != Z_OK)
55         {
56           free(out);
57           return 0;
58         }
59     }
60   outl += 4096 - strm.avail_out;
61   inflateEnd(&strm);
62   *outlp = outl;
63   return out;
64 }
65
66 static Id
67 parseonedep(Pool *pool, char *p)
68 {
69   char *n, *ne, *e, *ee;
70   Id name, evr;
71   int flags;
72
73   while (*p == ' ' || *p == '\t' || *p == '\n')
74     p++;
75   if (!*p || *p == '(')
76     return 0;
77   n = p;
78   /* find end of name */
79   while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
80     p++;
81   ne = p;
82   while (*p == ' ' || *p == '\t' || *p == '\n')
83     p++;
84   evr = 0;
85   flags = 0;
86   e = ee = 0;
87   if (*p == '(')
88     {
89       p++;
90       while (*p == ' ' || *p == '\t' || *p == '\n')
91         p++;
92       if (*p == '>')
93         flags |= REL_GT;
94       else if (*p == '=')
95         flags |= REL_EQ;
96       else if (*p == '<')
97         flags |= REL_LT;
98       if (flags)
99         {
100           p++;
101           if (*p == '>')
102             flags |= REL_GT;
103           else if (*p == '=')
104             flags |= REL_EQ;
105           else if (*p == '<')
106             flags |= REL_LT;
107           else
108             p--;
109           p++;
110         }
111       while (*p == ' ' || *p == '\t' || *p == '\n')
112         p++;
113       e = p;
114       while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
115         p++;
116       ee = p;
117       while (*p && *p != ')')
118         p++;
119       if (*p)
120         p++;
121       while (*p == ' ' || *p == '\t' || *p == '\n')
122         p++;
123     }
124   name = pool_strn2id(pool, n, ne - n, 1);
125   if (e)
126     {
127       evr = pool_strn2id(pool, e, ee - e, 1);
128       name = pool_rel2id(pool, name, evr, flags, 1);
129     }
130   if (*p == '|')
131     {
132       Id id = parseonedep(pool, p + 1);
133       if (id)
134         name = pool_rel2id(pool, name, id, REL_OR, 1);
135     }
136   return name;
137 }
138
139 static unsigned int
140 makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
141 {
142   Pool *pool = repo->pool;
143   char *p;
144   Id id;
145
146   while ((p = strchr(deps, ',')) != 0)
147     {
148       *p = 0;
149       olddeps = makedeps(repo, deps, olddeps, marker);
150       *p = ',';
151       deps = p + 1;
152     }
153   id = parseonedep(pool, deps);
154   if (!id)
155     return olddeps;
156   return repo_addid_dep(repo, olddeps, id, marker);
157 }
158
159
160 /* put data from control file into the solvable */
161 /* warning: does inplace changes */
162 static void
163 control2solvable(Solvable *s, Repodata *data, char *control)
164 {
165   Repo *repo = s->repo;
166   Pool *pool = repo->pool;
167   char *p, *q, *end, *tag;
168   int x, l;
169   int havesource = 0;
170   char checksum[32 * 2 + 1];
171   Id checksumtype = 0;
172   Id newtype;
173
174   p = control;
175   while (*p)
176     {
177       p = strchr(p, '\n');
178       if (!p)
179         break;
180       if (p[1] == ' ' || p[1] == '\t')
181         {
182           char *q;
183           /* continuation line */
184           q = p - 1;
185           while (q >= control && *q == ' ' && *q == '\t')
186             q--;
187           l = q + 1 - control;
188           if (l)
189             memmove(p + 1 - l, control, l);
190           control = p + 1 - l;
191           p[1] = '\n';
192           p += 2;
193           continue;
194         }
195       end = p - 1;
196       if (*p)
197         *p++ = 0;
198       /* strip trailing space */
199       while (end >= control && *end == ' ' && *end == '\t')
200         *end-- = 0;
201       tag = control;
202       control = p;
203       q = strchr(tag, ':');
204       if (!q || q - tag < 4)
205         continue;
206       *q++ = 0;
207       while (*q == ' ' || *q == '\t')
208         q++;
209       x = '@' + (tag[0] & 0x1f);
210       x = (x << 8) + '@' + (tag[1] & 0x1f);
211       switch(x)
212         {
213         case 'A' << 8 | 'R':
214           if (!strcasecmp(tag, "architecture"))
215             s->arch = pool_str2id(pool, q, 1);
216           break;
217         case 'B' << 8 | 'R':
218           if (!strcasecmp(tag, "breaks"))
219             s->conflicts = makedeps(repo, q, s->conflicts, 0);
220           break;
221         case 'C' << 8 | 'O':
222           if (!strcasecmp(tag, "conflicts"))
223             s->conflicts = makedeps(repo, q, s->conflicts, 0);
224           break;
225         case 'D' << 8 | 'E':
226           if (!strcasecmp(tag, "depends"))
227             s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
228           else if (!strcasecmp(tag, "description"))
229             {
230               char *ld = strchr(q, '\n');
231               if (ld)
232                 {
233                   *ld++ = 0;
234                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
235                 }
236               else
237                 repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
238               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
239             }
240           break;
241         case 'E' << 8 | 'N':
242           if (!strcasecmp(tag, "enhances"))
243             s->enhances = makedeps(repo, q, s->enhances, 0);
244           break;
245         case 'F' << 8 | 'I':
246           if (!strcasecmp(tag, "filename"))
247             repodata_set_location(data, s - pool->solvables, 0, 0, q);
248           break;
249         case 'H' << 8 | 'O':
250           if (!strcasecmp(tag, "homepage"))
251             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
252           break;
253         case 'I' << 8 | 'N':
254           if (!strcasecmp(tag, "installed-size"))
255             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, atoi(q));
256           break;
257         case 'M' << 8 | 'D':
258           if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
259             {
260               strcpy(checksum, q);
261               checksumtype = REPOKEY_TYPE_MD5;
262             }
263           break;
264         case 'P' << 8 | 'A':
265           if (!strcasecmp(tag, "package"))
266             s->name = pool_str2id(pool, q, 1);
267           break;
268         case 'P' << 8 | 'R':
269           if (!strcasecmp(tag, "pre-depends"))
270             s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
271           else if (!strcasecmp(tag, "provides"))
272             s->provides = makedeps(repo, q, s->provides, 0);
273           break;
274         case 'R' << 8 | 'E':
275           if (!strcasecmp(tag, "replaces"))
276             s->obsoletes = makedeps(repo, q, s->conflicts, 0);
277           else if (!strcasecmp(tag, "recommends"))
278             s->recommends = makedeps(repo, q, s->recommends, 0);
279           break;
280         case 'S' << 8 | 'H':
281           newtype = solv_chksum_str2type(tag);
282           if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
283             break;
284           if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
285             {
286               strcpy(checksum, q);
287               checksumtype = newtype;
288             }
289           break;
290         case 'S' << 8 | 'O':
291           if (!strcasecmp(tag, "source"))
292             {
293               char *q2;
294               /* ignore version for now */
295               for (q2 = q; *q2; q2++)
296                 if (*q2 == ' ' || *q2 == '\t')
297                   {
298                     *q2 = 0;
299                     break;
300                   }
301               if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
302                 repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
303               else
304                 repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
305               havesource = 1;
306             }
307           break;
308         case 'S' << 8 | 'U':
309           if (!strcasecmp(tag, "suggests"))
310             s->suggests = makedeps(repo, q, s->suggests, 0);
311           break;
312         case 'V' << 8 | 'E':
313           if (!strcasecmp(tag, "version"))
314             s->evr = pool_str2id(pool, q, 1);
315           break;
316         }
317     }
318   if (checksumtype)
319     repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
320   if (!s->arch)
321     s->arch = ARCH_ALL;
322   if (!s->evr)
323     s->evr = ID_EMPTY;
324   if (s->name)
325     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
326   if (s->name && !havesource)
327     repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
328   if (s->obsoletes)
329     {
330       /* obsoletes only count when the packages also conflict */
331       int i, j, k;
332       Id d;
333       for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
334         {
335           if (s->conflicts)
336             {
337               for (k = s->conflicts; repo->idarraydata[k] != 0; k++)
338                 if (repo->idarraydata[k] == d)
339                   break;
340               if (repo->idarraydata[k])
341                 {
342                   repo->idarraydata[j++] = d;
343                 }
344             }
345         }
346       repo->idarraydata[j] = 0;
347     }
348 }
349
350 int
351 repo_add_debpackages(Repo *repo, FILE *fp, int flags)
352 {
353   Pool *pool = repo->pool;
354   Repodata *data;
355   char *buf, *p;
356   int bufl, l, ll;
357   Solvable *s;
358
359   data = repo_add_repodata(repo, flags);
360   buf = solv_malloc(4096);
361   bufl = 4096;
362   l = 0;
363   buf[l] = 0;
364   p = buf;
365   for (;;)
366     {
367       if (!(p = strchr(p, '\n')))
368         {
369           int l3;
370           if (l + 1024 >= bufl)
371             {
372               buf = solv_realloc(buf, bufl + 4096);
373               bufl += 4096;
374               p = buf + l;
375               continue;
376             }
377           p = buf + l;
378           ll = fread(p, 1, bufl - l - 1, fp);
379           if (ll <= 0)
380             break;
381           p[ll] = 0;
382           while ((l3 = strlen(p)) < ll)
383             p[l3] = '\n';
384           l += ll;
385           continue;
386         }
387       p++;
388       if (*p != '\n')
389         continue;
390       *p = 0;
391       ll = p - buf + 1;
392       s = pool_id2solvable(pool, repo_add_solvable(repo));
393       control2solvable(s, data, buf);
394       if (!s->name)
395         repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
396       if (l > ll)
397         memmove(buf, p + 1, l - ll);
398       l -= ll;
399       p = buf;
400       buf[l] = 0;
401     }
402   if (l)
403     {
404       s = pool_id2solvable(pool, repo_add_solvable(repo));
405       control2solvable(s, data, buf);
406       if (!s->name)
407         repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
408     }
409   solv_free(buf);
410   if (!(flags & REPO_NO_INTERNALIZE))
411     repodata_internalize(data);
412   return 0;
413 }
414
415 int
416 repo_add_debdb(Repo *repo, const char *rootdir, int flags)
417 {
418   FILE *fp;
419   const char *path = "/var/lib/dpkg/status";
420   if (rootdir)
421     path = pool_tmpjoin(repo->pool, rootdir, path, 0);
422   if ((fp = fopen(path, "r")) == 0)
423     {
424       perror(path);
425       exit(1);
426     }
427   repo_add_debpackages(repo, fp, flags);
428   fclose(fp);
429   return 0;
430 }
431
432 int
433 repo_add_debs(Repo *repo, const char **debs, int ndebs, int flags)
434 {
435   Pool *pool = repo->pool;
436   Repodata *data;
437   unsigned char buf[4096], *bp;
438   int i, l, l2, vlen, clen, ctarlen;
439   unsigned char *ctgz;
440   unsigned char pkgid[16];
441   unsigned char *ctar;
442   int gotpkgid;
443   FILE *fp;
444   Solvable *s;
445   struct stat stb;
446
447   data = repo_add_repodata(repo, flags);
448   for (i = 0; i < ndebs; i++)
449     {
450       if ((fp = fopen(debs[i], "r")) == 0)
451         {
452           perror(debs[i]);
453           continue;
454         }
455       if (fstat(fileno(fp), &stb))
456         {
457           perror("stat");
458           continue;
459         }
460       l = fread(buf, 1, sizeof(buf), fp);
461       if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0)
462         {
463           fprintf(stderr, "%s: not a deb package\n", debs[i]);
464           fclose(fp);
465           continue;
466         }
467       vlen = atoi((char *)buf + 8 + 48);
468       if (vlen < 0 || vlen > l)
469         {
470           fprintf(stderr, "%s: not a deb package\n", debs[i]);
471           fclose(fp);
472           continue;
473         }
474       vlen += vlen & 1;
475       if (l < 8 + 60 + vlen + 60)
476         {
477           fprintf(stderr, "%s: unhandled deb package\n", debs[i]);
478           fclose(fp);
479           continue;
480         }
481       if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0)
482         {
483           fprintf(stderr, "%s: control.tar.gz is not second entry\n", debs[i]);
484           fclose(fp);
485           continue;
486         }
487       clen = atoi((char *)buf + 8 + 60 + vlen + 48);
488       if (clen <= 0)
489         {
490           fprintf(stderr, "%s: control.tar.gz has illegal size\n", debs[i]);
491           fclose(fp);
492           continue;
493         }
494       ctgz = solv_calloc(1, clen + 4);
495       bp = buf + 8 + 60 + vlen + 60;
496       l -= 8 + 60 + vlen + 60;
497       if (l > clen)
498         l = clen;
499       if (l)
500         memcpy(ctgz, bp, l);
501       if (l < clen)
502         {
503           if (fread(ctgz + l, clen - l, 1, fp) != 1)
504             {
505               fprintf(stderr, "%s: unexpected EOF\n", debs[i]);
506               solv_free(ctgz);
507               fclose(fp);
508               continue;
509             }
510         }
511       fclose(fp);
512       gotpkgid = 0;
513       if (flags & DEBS_ADD_WITH_PKGID)
514         {
515           void *handle = solv_chksum_create(REPOKEY_TYPE_MD5);
516           solv_chksum_add(handle, ctgz, clen);
517           solv_chksum_free(handle, pkgid);
518           gotpkgid = 1;
519         }
520       if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
521         {
522           fprintf(stderr, "%s: control.tar.gz is not gzipped\n", debs[i]);
523           solv_free(ctgz);
524           continue;
525         }
526       if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
527         {
528           fprintf(stderr, "%s: control.tar.gz is compressed in a strange way\n", debs[i]);
529           solv_free(ctgz);
530           continue;
531         }
532       bp = ctgz + 4;
533       bp += 6;  /* skip time, xflags and OS code */
534       if (ctgz[3] & 0x04)
535         {
536           /* skip extra field */
537           l = bp[0] | bp[1] << 8;
538           bp += l + 2;
539           if (bp >= ctgz + clen)
540             {
541               fprintf(stderr, "%s: corrupt gzip\n", debs[i]);
542               solv_free(ctgz);
543               continue;
544             }
545         }
546       if (ctgz[3] & 0x08)       /* orig filename */
547         while (*bp)
548           bp++;
549       if (ctgz[3] & 0x10)       /* file comment */
550         while (*bp)
551           bp++;
552       if (ctgz[3] & 0x02)       /* header crc */
553         bp += 2;
554       if (bp >= ctgz + clen)
555         {
556           fprintf(stderr, "%s: corrupt control.tar.gz\n", debs[i]);
557           solv_free(ctgz);
558           continue;
559         }
560       ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
561       solv_free(ctgz);
562       if (!ctar)
563         {
564           fprintf(stderr, "%s: corrupt control.tar.gz\n", debs[i]);
565           continue;
566         }
567       bp = ctar;
568       l = ctarlen;
569       while (l > 512)
570         {
571           int j;
572           l2 = 0;
573           for (j = 124; j < 124 + 12; j++)
574             if (bp[j] >= '0' && bp[j] <= '7')
575               l2 = l2 * 8 + (bp[j] - '0');
576           if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
577             break;
578           l2 = 512 + ((l2 + 511) & ~511);
579           l -= l2;
580           bp += l2;
581         }
582       if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
583         {
584           fprintf(stderr, "%s: control.tar.gz contains no control file\n", debs[i]);
585           free(ctar);
586           continue;
587         }
588       memmove(ctar, bp + 512, l2);
589       ctar = solv_realloc(ctar, l2 + 1);
590       ctar[l2] = 0;
591       s = pool_id2solvable(pool, repo_add_solvable(repo));
592       control2solvable(s, data, (char *)ctar);
593       repodata_set_location(data, s - pool->solvables, 0, 0, debs[i]);
594       if (S_ISREG(stb.st_mode))
595         repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned int)((stb.st_size + 1023) / 1024));
596       if (gotpkgid)
597         repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
598       solv_free(ctar);
599     }
600   if (!(flags & REPO_NO_INTERNALIZE))
601     repodata_internalize(data);
602   return 0;
603 }
604
605 Id
606 repo_add_deb(Repo *repo, const char *deb, int flags)
607 {
608   int end = repo->end;
609   repo_add_debs(repo, &deb, 1, flags);
610   if (end == repo->end)
611     return 0;
612   else
613     return repo->end - 1;
614 }