db.h can be found under /usr/include on Fedora, etc.
[platform/upstream/libsolv.git] / ext / repo_rpmdb.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_rpmdb
10  *
11  * convert rpm db to repo
12  *
13  */
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <assert.h>
24
25 #include <rpm/rpmio.h>
26 #include <rpm/rpmpgp.h>
27 #include <rpm/header.h>
28 #include <rpm/rpmdb.h>
29
30 #ifndef DB_CREATE
31 # ifdef FEDORA
32 #  include <db.h>
33 # else
34 #  include <rpm/db.h>
35 # endif
36 #endif
37
38 #include "pool.h"
39 #include "repo.h"
40 #include "hash.h"
41 #include "util.h"
42 #include "queue.h"
43 #include "chksum.h"
44 #include "repo_rpmdb.h"
45
46 /* 3: added triggers */
47 #define RPMDB_COOKIE_VERSION 3
48
49 #define TAG_NAME                1000
50 #define TAG_VERSION             1001
51 #define TAG_RELEASE             1002
52 #define TAG_EPOCH               1003
53 #define TAG_SUMMARY             1004
54 #define TAG_DESCRIPTION         1005
55 #define TAG_BUILDTIME           1006
56 #define TAG_BUILDHOST           1007
57 #define TAG_INSTALLTIME         1008
58 #define TAG_SIZE                1009
59 #define TAG_DISTRIBUTION        1010
60 #define TAG_VENDOR              1011
61 #define TAG_LICENSE             1014
62 #define TAG_PACKAGER            1015
63 #define TAG_GROUP               1016
64 #define TAG_URL                 1020
65 #define TAG_ARCH                1022
66 #define TAG_FILESIZES           1028
67 #define TAG_FILEMODES           1030
68 #define TAG_FILEMD5S            1035
69 #define TAG_FILELINKTOS         1036
70 #define TAG_SOURCERPM           1044
71 #define TAG_PROVIDENAME         1047
72 #define TAG_REQUIREFLAGS        1048
73 #define TAG_REQUIRENAME         1049
74 #define TAG_REQUIREVERSION      1050
75 #define TAG_NOSOURCE            1051
76 #define TAG_NOPATCH             1052
77 #define TAG_CONFLICTFLAGS       1053
78 #define TAG_CONFLICTNAME        1054
79 #define TAG_CONFLICTVERSION     1055
80 #define TAG_TRIGGERNAME         1066
81 #define TAG_TRIGGERVERSION      1067
82 #define TAG_TRIGGERFLAGS        1068
83 #define TAG_OBSOLETENAME        1090
84 #define TAG_FILEDEVICES         1095
85 #define TAG_FILEINODES          1096
86 #define TAG_PROVIDEFLAGS        1112
87 #define TAG_PROVIDEVERSION      1113
88 #define TAG_OBSOLETEFLAGS       1114
89 #define TAG_OBSOLETEVERSION     1115
90 #define TAG_DIRINDEXES          1116
91 #define TAG_BASENAMES           1117
92 #define TAG_DIRNAMES            1118
93 #define TAG_PAYLOADFORMAT       1124
94 #define TAG_PATCHESNAME         1133
95 #define TAG_FILECOLORS          1140
96 #define TAG_SUGGESTSNAME        1156
97 #define TAG_SUGGESTSVERSION     1157
98 #define TAG_SUGGESTSFLAGS       1158
99 #define TAG_ENHANCESNAME        1159
100 #define TAG_ENHANCESVERSION     1160
101 #define TAG_ENHANCESFLAGS       1161
102
103 #define SIGTAG_SIZE             1000
104 #define SIGTAG_PGP              1002    /* RSA signature */
105 #define SIGTAG_MD5              1004    /* header+payload md5 checksum */
106 #define SIGTAG_GPG              1005    /* DSA signature */
107
108 #define DEP_LESS                (1 << 1)
109 #define DEP_GREATER             (1 << 2)
110 #define DEP_EQUAL               (1 << 3)
111 #define DEP_STRONG              (1 << 27)
112 #define DEP_PRE                 ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
113
114
115 struct rpmid {
116   unsigned int dbid;
117   char *name;
118 };
119
120 typedef struct rpmhead {
121   int cnt;
122   int dcnt;
123   unsigned char *dp;
124   unsigned char data[1];
125 } RpmHead;
126
127
128 static inline unsigned char *
129 headfindtag(RpmHead *h, int tag)
130 {
131   unsigned int i;
132   unsigned char *d, taga[4];
133   d = h->dp - 16;
134   taga[0] = tag >> 24;
135   taga[1] = tag >> 16;
136   taga[2] = tag >> 8;
137   taga[3] = tag;
138   for (i = 0; i < h->cnt; i++, d -= 16)
139     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
140       return d;
141   return 0;
142 }
143
144 static int
145 headexists(RpmHead *h, int tag)
146 {
147   return headfindtag(h, tag) ? 1 : 0;
148 }
149
150 static unsigned int *
151 headint32array(RpmHead *h, int tag, int *cnt)
152 {
153   unsigned int i, o, *r;
154   unsigned char *d = headfindtag(h, tag);
155
156   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
157     return 0;
158   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
159   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
160   if (o + 4 * i > h->dcnt)
161     return 0;
162   d = h->dp + o;
163   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
164   if (cnt)
165     *cnt = i;
166   for (o = 0; o < i; o++, d += 4)
167     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
168   return r;
169 }
170
171 /* returns the first entry of an integer array */
172 static unsigned int
173 headint32(RpmHead *h, int tag)
174 {
175   unsigned int i, o;
176   unsigned char *d = headfindtag(h, tag);
177
178   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
179     return 0;
180   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
181   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
182   if (i == 0 || o + 4 * i > h->dcnt)
183     return 0;
184   d = h->dp + o;
185   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
186 }
187
188 static unsigned int *
189 headint16array(RpmHead *h, int tag, int *cnt)
190 {
191   unsigned int i, o, *r;
192   unsigned char *d = headfindtag(h, tag);
193
194   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
195     return 0;
196   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
197   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
198   if (o + 4 * i > h->dcnt)
199     return 0;
200   d = h->dp + o;
201   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
202   if (cnt)
203     *cnt = i;
204   for (o = 0; o < i; o++, d += 2)
205     r[o] = d[0] << 8 | d[1];
206   return r;
207 }
208
209 static char *
210 headstring(RpmHead *h, int tag)
211 {
212   unsigned int o;
213   unsigned char *d = headfindtag(h, tag);
214   /* 6: STRING, 9: I18NSTRING */
215   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
216     return 0;
217   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
218   if (o >= h->dcnt)
219     return 0;
220   return (char *)h->dp + o;
221 }
222
223 static char **
224 headstringarray(RpmHead *h, int tag, int *cnt)
225 {
226   unsigned int i, o;
227   unsigned char *d = headfindtag(h, tag);
228   char **r;
229
230   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
231     return 0;
232   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
233   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
234   r = sat_calloc(i ? i : 1, sizeof(char *));
235   if (cnt)
236     *cnt = i;
237   d = h->dp + o;
238   for (o = 0; o < i; o++)
239     {
240       r[o] = (char *)d;
241       if (o + 1 < i)
242         d += strlen((char *)d) + 1;
243       if (d >= h->dp + h->dcnt)
244         {
245           sat_free(r);
246           return 0;
247         }
248     }
249   return r;
250 }
251
252 static unsigned char *
253 headbinary(RpmHead *h, int tag, unsigned int *sizep)
254 {
255   unsigned int i, o;
256   unsigned char *d = headfindtag(h, tag);
257   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 7)
258     return 0;
259   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
260   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
261   if (o > h->dcnt || o + i < o || o + i > h->dcnt)
262     return 0;
263   if (sizep)
264     *sizep = i;
265   return h->dp + o;
266 }
267
268 static char *headtoevr(RpmHead *h)
269 {
270   unsigned int epoch;
271   char *version, *v;
272   char *release;
273   char *evr;
274
275   version  = headstring(h, TAG_VERSION);
276   release  = headstring(h, TAG_RELEASE);
277   epoch = headint32(h, TAG_EPOCH);
278   if (!version || !release)
279     {
280       fprintf(stderr, "headtoevr: bad rpm header\n");
281       exit(1);
282     }
283   for (v = version; *v >= 0 && *v <= '9'; v++)
284     ;
285   if (epoch || (v != version && *v == ':'))
286     {
287       char epochbuf[11];        /* 32bit decimal will fit in */
288       sprintf(epochbuf, "%u", epoch);
289       evr = sat_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
290       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
291     }
292   else
293     {
294       evr = sat_malloc(strlen(version) + 1 + strlen(release) + 1);
295       sprintf(evr, "%s-%s", version, release);
296     }
297   return evr;
298 }
299
300
301 static void
302 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
303 {
304   const unsigned char *cp;
305   int state = 0;
306   int c;
307   unsigned char *buf = 0, *bp;
308
309   /* check if it's already utf8, code taken from screen ;-) */
310   cp = (const unsigned char *)str;
311   while ((c = *cp++) != 0)
312     {
313       if (state)
314         {
315           if ((c & 0xc0) != 0x80)
316             break; /* encoding error */
317           c = (c & 0x3f) | (state << 6);
318           if (!(state & 0x40000000))
319             {
320               /* check for overlong sequences */
321               if ((c & 0x820823e0) == 0x80000000)
322                 c = 0xfdffffff;
323               else if ((c & 0x020821f0) == 0x02000000)
324                 c = 0xfff7ffff;
325               else if ((c & 0x000820f8) == 0x00080000)
326                 c = 0xffffd000;
327               else if ((c & 0x0000207c) == 0x00002000)
328                 c = 0xffffff70;
329             }
330         }
331       else
332         {
333           /* new sequence */
334           if (c >= 0xfe)
335             break;
336           else if (c >= 0xfc)
337             c = (c & 0x01) | 0xbffffffc;    /* 5 bytes to follow */
338           else if (c >= 0xf8)
339             c = (c & 0x03) | 0xbfffff00;    /* 4 */
340           else if (c >= 0xf0)
341             c = (c & 0x07) | 0xbfffc000;    /* 3 */
342           else if (c >= 0xe0)
343             c = (c & 0x0f) | 0xbff00000;    /* 2 */
344           else if (c >= 0xc2)
345             c = (c & 0x1f) | 0xfc000000;    /* 1 */
346           else if (c >= 0x80)
347             break;
348         }
349       state = (c & 0x80000000) ? c : 0;
350     }
351   if (c)
352     {
353       /* not utf8, assume latin1 */
354       buf = sat_malloc(2 * strlen(str) + 1);
355       cp = (const unsigned char *)str;
356       str = (char *)buf;
357       bp = buf;
358       while ((c = *cp++) != 0)
359         {
360           if (c >= 0xc0)
361             {
362               *bp++ = 0xc3;
363               c ^= 0x80;
364             }
365           else if (c >= 0x80)
366             *bp++ = 0xc2;
367           *bp++ = c;
368         }
369       *bp++ = 0;
370     }
371   repodata_set_str(repodata, handle, tag, str);
372   if (buf)
373     sat_free(buf);
374 }
375
376
377 #define MAKEDEPS_FILTER_WEAK    (1 << 0)
378 #define MAKEDEPS_FILTER_STRONG  (1 << 1)
379 #define MAKEDEPS_NO_RPMLIB      (1 << 2)
380
381 /*
382  * strong: 0: ignore strongness
383  *         1: filter to strong
384  *         2: filter to weak
385  */
386 static unsigned int
387 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int flags)
388 {
389   char **n, **v;
390   unsigned int *f;
391   int i, cc, nc, vc, fc;
392   int haspre;
393   unsigned int olddeps;
394   Id *ida;
395   int strong;
396
397   strong = flags & (MAKEDEPS_FILTER_STRONG|MAKEDEPS_FILTER_WEAK);
398   n = headstringarray(rpmhead, tagn, &nc);
399   if (!n)
400     return 0;
401   v = headstringarray(rpmhead, tagv, &vc);
402   if (!v)
403     {
404       sat_free(n);
405       return 0;
406     }
407   f = headint32array(rpmhead, tagf, &fc);
408   if (!f)
409     {
410       sat_free(n);
411       free(v);
412       return 0;
413     }
414   if (nc != vc || nc != fc)
415     {
416       fprintf(stderr, "bad dependency entries\n");
417       exit(1);
418     }
419
420   cc = nc;
421   haspre = 0;   /* add no prereq marker */
422   if (flags)
423     {
424       /* we do filtering */
425       cc = 0;
426       for (i = 0; i < nc; i++)
427         {
428           if (strong && (f[i] & DEP_STRONG) != (strong == MAKEDEPS_FILTER_WEAK ? 0 : DEP_STRONG))
429             continue;
430           if ((flags & MAKEDEPS_NO_RPMLIB) != 0)
431             if (!strncmp(n[i], "rpmlib(", 7))
432               continue;
433           if ((f[i] & DEP_PRE) != 0)
434             haspre = 1;
435           cc++;
436         }
437     }
438   else if (tagn == TAG_REQUIRENAME)
439     {
440       /* no filtering, just look for the first prereq */
441       for (i = 0; i < nc; i++)
442         if ((f[i] & DEP_PRE) != 0)
443           {
444             haspre = 1;
445             break;
446           }
447     }
448   if (cc == 0)
449     {
450       sat_free(n);
451       sat_free(v);
452       sat_free(f);
453       return 0;
454     }
455   cc += haspre;
456   olddeps = repo_reserve_ids(repo, 0, cc);
457   ida = repo->idarraydata + olddeps;
458   for (i = 0; ; i++)
459     {
460       if (i == nc)
461         {
462           if (haspre != 1)
463             break;
464           haspre = 2;   /* pass two: prereqs */
465           i = 0;
466           *ida++ = SOLVABLE_PREREQMARKER;
467         }
468       if (strong && (f[i] & DEP_STRONG) != (strong == MAKEDEPS_FILTER_WEAK ? 0 : DEP_STRONG))
469         continue;
470       if (haspre == 1 && (f[i] & DEP_PRE) != 0)
471         continue;
472       if (haspre == 2 && (f[i] & DEP_PRE) == 0)
473         continue;
474       if ((flags & MAKEDEPS_NO_RPMLIB) != 0)
475         if (!strncmp(n[i], "rpmlib(", 7))
476           continue;
477       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
478         {
479           Id name, evr;
480           int flags = 0;
481           if ((f[i] & DEP_LESS) != 0)
482             flags |= 4;
483           if ((f[i] & DEP_EQUAL) != 0)
484             flags |= 2;
485           if ((f[i] & DEP_GREATER) != 0)
486             flags |= 1;
487           name = str2id(pool, n[i], 1);
488           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
489             evr = str2id(pool, v[i] + 2, 1);
490           else
491             evr = str2id(pool, v[i], 1);
492           *ida++ = rel2id(pool, name, evr, flags, 1);
493         }
494       else
495         *ida++ = str2id(pool, n[i], 1);
496     }
497   *ida++ = 0;
498   repo->idarraysize += cc + 1;
499   sat_free(n);
500   sat_free(v);
501   sat_free(f);
502   return olddeps;
503 }
504
505
506 #ifdef USE_FILEFILTER
507
508 #define FILEFILTER_EXACT    0
509 #define FILEFILTER_STARTS   1
510 #define FILEFILTER_CONTAINS 2
511
512 struct filefilter {
513   int dirmatch;
514   char *dir;
515   char *base;
516 };
517
518 static struct filefilter filefilters[] = {
519   { FILEFILTER_CONTAINS, "/bin/", 0},
520   { FILEFILTER_CONTAINS, "/sbin/", 0},
521   { FILEFILTER_CONTAINS, "/lib/", 0},
522   { FILEFILTER_CONTAINS, "/lib64/", 0},
523   { FILEFILTER_CONTAINS, "/etc/", 0},
524   { FILEFILTER_STARTS, "/usr/games/", 0},
525   { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
526   { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
527   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
528 };
529
530 #endif
531
532 static void
533 adddudata(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dc)
534 {
535   Id handle, did;
536   int i, fszc;
537   unsigned int *fkb, *fn, *fsz, *fm, *fino;
538   unsigned int inotest[256], inotestok;
539
540   if (!fc)
541     return;
542   fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc);
543   if (!fsz || fc != fszc)
544     {
545       sat_free(fsz);
546       return;
547     }
548   /* stupid rpm records sizes of directories, so we have to check the mode */
549   fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
550   if (!fm || fc != fszc)
551     {
552       sat_free(fsz);
553       sat_free(fm);
554       return;
555     }
556   fino = headint32array(rpmhead, TAG_FILEINODES, &fszc);
557   if (!fino || fc != fszc)
558     {
559       sat_free(fsz);
560       sat_free(fm);
561       sat_free(fino);
562       return;
563     }
564   inotestok = 0;
565   if (fc < sizeof(inotest))
566     {
567       memset(inotest, 0, sizeof(inotest));
568       for (i = 0; i < fc; i++)
569         {
570           int off, bit;
571           if (fsz[i] == 0 || !S_ISREG(fm[i]))
572             continue;
573           off = (fino[i] >> 5) & (sizeof(inotest)/sizeof(*inotest) - 1);
574           bit = 1 << (fino[i] & 31);
575           if ((inotest[off] & bit) != 0)
576             break;
577           inotest[off] |= bit;
578         }
579       if (i == fc)
580         inotestok = 1;
581     }
582   if (!inotestok)
583     {
584       unsigned int *fdev = headint32array(rpmhead, TAG_FILEDEVICES, &fszc);
585       unsigned int *fx, j;
586       unsigned int mask, hash, hh;
587       if (!fdev || fc != fszc)
588         {
589           sat_free(fsz);
590           sat_free(fm);
591           sat_free(fdev);
592           sat_free(fino);
593           return;
594         }
595       mask = fc;
596       while ((mask & (mask - 1)) != 0)
597         mask = mask & (mask - 1);
598       mask <<= 2;
599       if (mask > sizeof(inotest)/sizeof(*inotest))
600         fx = sat_calloc(mask, sizeof(unsigned int));
601       else
602         {
603           fx = inotest;
604           memset(fx, 0, mask * sizeof(unsigned int));
605         }
606       mask--;
607       for (i = 0; i < fc; i++)
608         {
609           if (fsz[i] == 0 || !S_ISREG(fm[i]))
610             continue;
611           hash = (fino[i] + fdev[i] * 31) & mask;
612           hh = 7;
613           while ((j = fx[hash]) != 0)
614             {
615               if (fino[j - 1] == fino[i] && fdev[j - 1] == fdev[i])
616                 {
617                   fsz[i] = 0;   /* kill entry */
618                   break;
619                 }
620               hash = (hash + hh++) & mask;
621             }
622           if (!j)
623             fx[hash] = i + 1;
624         }
625       if (fx != inotest)
626         sat_free(fx);
627       sat_free(fdev);
628     }
629   sat_free(fino);
630   fn = sat_calloc(dc, sizeof(unsigned int));
631   fkb = sat_calloc(dc, sizeof(unsigned int));
632   for (i = 0; i < fc; i++)
633     {
634       if (di[i] >= dc)
635         continue;
636       fn[di[i]]++;
637       if (fsz[i] == 0 || !S_ISREG(fm[i]))
638         continue;
639       fkb[di[i]] += fsz[i] / 1024 + 1;
640     }
641   sat_free(fsz);
642   sat_free(fm);
643   /* commit */
644   handle = s - pool->solvables;
645   for (i = 0; i < dc; i++)
646     {
647       if (!fn[i])
648         continue;
649       if (!*dn[i])
650         {
651           if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
652             did = repodata_str2dir(data, "/usr/src", 1);
653           else
654             continue;   /* work around rpm bug */
655         }
656       else
657         did = repodata_str2dir(data, dn[i], 1);
658       repodata_add_dirnumnum(data, handle, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
659     }
660   sat_free(fn);
661   sat_free(fkb);
662 }
663
664 /* assumes last processed array is provides! */
665 static unsigned int
666 addfileprovides(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, unsigned int olddeps)
667 {
668   char **bn;
669   char **dn;
670   unsigned int *di;
671   int bnc, dnc, dic;
672   int i;
673 #ifdef USE_FILEFILTER
674   int j;
675   struct filefilter *ff;
676 #endif
677 #if 0
678   char *fn = 0;
679   int fna = 0;
680 #endif
681
682   if (!data)
683     return olddeps;
684   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
685   if (!bn)
686     return olddeps;
687   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
688   if (!dn)
689     {
690       sat_free(bn);
691       return olddeps;
692     }
693   di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
694   if (!di)
695     {
696       sat_free(bn);
697       sat_free(dn);
698       return olddeps;
699     }
700   if (bnc != dic)
701     {
702       fprintf(stderr, "bad filelist\n");
703       exit(1);
704     }
705
706   if (data)
707     adddudata(pool, repo, data, s, rpmhead, dn, di, bnc, dnc);
708
709   for (i = 0; i < bnc; i++)
710     {
711 #ifdef USE_FILEFILTER
712       ff = filefilters;
713       for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
714         {
715           if (ff->dir)
716             {
717               switch (ff->dirmatch)
718                 {
719                 case FILEFILTER_STARTS:
720                   if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
721                     continue;
722                   break;
723                 case FILEFILTER_CONTAINS:
724                   if (!strstr(dn[di[i]], ff->dir))
725                     continue;
726                   break;
727                 case FILEFILTER_EXACT:
728                 default:
729                   if (strcmp(dn[di[i]], ff->dir))
730                     continue;
731                   break;
732                 }
733             }
734           if (ff->base)
735             {
736               if (strcmp(bn[i], ff->base))
737                 continue;
738             }
739           break;
740         }
741       if (j == sizeof(filefilters)/sizeof(*filefilters))
742         continue;
743 #endif
744 #if 0
745       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
746       if (j > fna)
747         {
748           fna = j + 256;
749           fn = sat_realloc(fn, fna);
750         }
751       strcpy(fn, dn[di[i]]);
752       strcat(fn, bn[i]);
753       olddeps = repo_addid_dep(repo, olddeps, str2id(pool, fn, 1), SOLVABLE_FILEMARKER);
754 #endif
755       if (data)
756         {
757           Id handle, did;
758           char *b = bn[i];
759
760           handle = s - pool->solvables;
761           did = repodata_str2dir(data, dn[di[i]], 1);
762           if (!did)
763             {
764               did = repodata_str2dir(data, "/", 1);
765               if (b && b[0] == '/')
766                 b++;    /* work around rpm bug */
767             }
768           repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, b);
769         }
770     }
771 #if 0
772   if (fn)
773     sat_free(fn);
774 #endif
775   sat_free(bn);
776   sat_free(dn);
777   sat_free(di);
778   return olddeps;
779 }
780
781 static void
782 addsourcerpm(Pool *pool, Repodata *data, Id handle, char *sourcerpm, char *name, char *evr)
783 {
784   const char *p, *sevr, *sarch;
785
786   p = strrchr(sourcerpm, '.');
787   if (!p || strcmp(p, ".rpm") != 0)
788     return;
789   p--;
790   while (p > sourcerpm && *p != '.')
791     p--;
792   if (*p != '.' || p == sourcerpm)
793     return;
794   sarch = p-- + 1;
795   while (p > sourcerpm && *p != '-')
796     p--;
797   if (*p != '-' || p == sourcerpm)
798     return;
799   p--;
800   while (p > sourcerpm && *p != '-')
801     p--;
802   if (*p != '-' || p == sourcerpm)
803     return;
804   sevr = p + 1;
805   if (!strcmp(sarch, "src.rpm"))
806     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_SRC);
807   else if (!strcmp(sarch, "nosrc.rpm"))
808     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
809   else
810     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, strn2id(pool, sarch, strlen(sarch) - 4, 1));
811   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
812     repodata_set_void(data, handle, SOLVABLE_SOURCEEVR);
813   else
814     repodata_set_id(data, handle, SOLVABLE_SOURCEEVR, strn2id(pool, sevr, sarch - sevr - 1, 1));
815   if (name && !strncmp(sourcerpm, name, sevr - sourcerpm - 1) && name[sevr - sourcerpm - 1] == 0)
816     repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
817   else
818     repodata_set_id(data, handle, SOLVABLE_SOURCENAME, strn2id(pool, sourcerpm, sevr - sourcerpm - 1, 1));
819 }
820
821 static int
822 rpm2solv(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, int flags)
823 {
824   char *name;
825   char *evr;
826   char *sourcerpm;
827
828   name = headstring(rpmhead, TAG_NAME);
829   if (!strcmp(name, "gpg-pubkey"))
830     return 0;
831   s->name = str2id(pool, name, 1);
832   if (!s->name)
833     {
834       fprintf(stderr, "package has no name\n");
835       exit(1);
836     }
837   sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
838   if (sourcerpm)
839     s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
840   else
841     {
842       if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
843         s->arch = ARCH_NOSRC;
844       else
845         s->arch = ARCH_SRC;
846     }
847   if (!s->arch)
848     s->arch = ARCH_NOARCH;
849   evr = headtoevr(rpmhead);
850   s->evr = str2id(pool, evr, 1);
851   s->vendor = str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
852
853   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
854   if ((flags & RPM_ADD_NO_FILELIST) == 0)
855     s->provides = addfileprovides(pool, repo, data, s, rpmhead, s->provides);
856   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
857     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
858   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, (flags & RPM_ADD_NO_RPMLIBREQS) ? MAKEDEPS_NO_RPMLIB : 0);
859   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
860   s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
861
862   s->recommends = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, MAKEDEPS_FILTER_STRONG);
863   s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, MAKEDEPS_FILTER_WEAK);
864   s->supplements = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, MAKEDEPS_FILTER_STRONG);
865   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, MAKEDEPS_FILTER_WEAK);
866   s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
867   s->conflicts = repo_fix_conflicts(repo, s->conflicts);
868
869   if (data)
870     {
871       Id handle;
872       char *str;
873       unsigned int u32;
874
875       handle = s - pool->solvables;
876       str = headstring(rpmhead, TAG_SUMMARY);
877       if (str)
878         setutf8string(data, handle, SOLVABLE_SUMMARY, str);
879       str = headstring(rpmhead, TAG_DESCRIPTION);
880       if (str)
881         {
882           char *aut, *p;
883           for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
884             if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
885               break;
886           if (aut)
887             {
888               /* oh my, found SUSE special author section */
889               int l = aut - str;
890               str = strdup(str);
891               aut = str + l;
892               str[l] = 0;
893               while (l > 0 && str[l - 1] == '\n')
894                 str[--l] = 0;
895               if (l)
896                 setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
897               p = aut + 19;
898               aut = str;        /* copy over */
899               while (*p == ' ' || *p == '\n')
900                 p++;
901               while (*p)
902                 {
903                   if (*p == '\n')
904                     {
905                       *aut++ = *p++;
906                       while (*p == ' ')
907                         p++;
908                       continue;
909                     }
910                   *aut++ = *p++;
911                 }
912               while (aut != str && aut[-1] == '\n')
913                 aut--;
914               *aut = 0;
915               if (*str)
916                 setutf8string(data, handle, SOLVABLE_AUTHORS, str);
917               free(str);
918             }
919           else if (*str)
920             setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
921         }
922       str = headstring(rpmhead, TAG_GROUP);
923       if (str)
924         repodata_set_poolstr(data, handle, SOLVABLE_GROUP, str);
925       str = headstring(rpmhead, TAG_LICENSE);
926       if (str)
927         repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, str);
928       str = headstring(rpmhead, TAG_URL);
929       if (str)
930         repodata_set_str(data, handle, SOLVABLE_URL, str);
931       str = headstring(rpmhead, TAG_DISTRIBUTION);
932       if (str)
933         repodata_set_poolstr(data, handle, SOLVABLE_DISTRIBUTION, str);
934       str = headstring(rpmhead, TAG_PACKAGER);
935       if (str)
936         repodata_set_poolstr(data, handle, SOLVABLE_PACKAGER, str);
937       u32 = headint32(rpmhead, TAG_BUILDTIME);
938       if (u32)
939         repodata_set_num(data, handle, SOLVABLE_BUILDTIME, u32);
940       u32 = headint32(rpmhead, TAG_INSTALLTIME);
941       if (u32)
942         repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, u32);
943       u32 = headint32(rpmhead, TAG_SIZE);
944       if (u32)
945         repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (u32 + 1023) / 1024);
946       if (sourcerpm)
947         addsourcerpm(pool, data, handle, sourcerpm, name, evr);
948       if ((flags & RPM_ADD_TRIGGERS) != 0)
949         {
950           Id id, lastid;
951           unsigned int ida = makedeps(pool, repo, rpmhead, TAG_TRIGGERNAME, TAG_TRIGGERVERSION, TAG_TRIGGERFLAGS, 0);
952
953           lastid = 0;
954           for (; (id = repo->idarraydata[ida]) != 0; ida++)
955             {
956               /* we currently do not support rel ids in incore data, so
957                * strip off versioning information */
958               while (ISRELDEP(id))
959                 {
960                   Reldep *rd = GETRELDEP(pool, id);
961                   id = rd->name;
962                 }
963               if (id == lastid)
964                 continue;
965               repodata_add_idarray(data, handle, SOLVABLE_TRIGGERS, id);
966               lastid = id;
967             }
968         }
969     }
970   sat_free(evr);
971   return 1;
972 }
973
974 static Id
975 copyreldep(Pool *pool, Pool *frompool, Id id)
976 {
977   Reldep *rd = GETRELDEP(frompool, id);
978   Id name = rd->name, evr = rd->evr;
979   if (ISRELDEP(name))
980     name = copyreldep(pool, frompool, name);
981   else
982     name = str2id(pool, id2str(frompool, name), 1);
983   if (ISRELDEP(evr))
984     evr = copyreldep(pool, frompool, evr);
985   else
986     evr = str2id(pool, id2str(frompool, evr), 1);
987   return rel2id(pool, name, evr, rd->flags, 1);
988 }
989
990 static Offset
991 copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
992 {
993   int cc;
994   Id id, *ida, *from;
995   Offset ido;
996   Pool *frompool = fromrepo->pool;
997
998   if (!fromoff)
999     return 0;
1000   from = fromrepo->idarraydata + fromoff;
1001   for (ida = from, cc = 0; *ida; ida++, cc++)
1002     ;
1003   if (cc == 0)
1004     return 0;
1005   ido = repo_reserve_ids(repo, 0, cc);
1006   ida = repo->idarraydata + ido;
1007   if (frompool && pool != frompool)
1008     {
1009       while (*from)
1010         {
1011           id = *from++;
1012           if (ISRELDEP(id))
1013             id = copyreldep(pool, frompool, id);
1014           else
1015             id = str2id(pool, id2str(frompool, id), 1);
1016           *ida++ = id;
1017         }
1018       *ida = 0;
1019     }
1020   else
1021     memcpy(ida, from, (cc + 1) * sizeof(Id));
1022   repo->idarraysize += cc + 1;
1023   return ido;
1024 }
1025
1026 #define COPYDIR_DIRCACHE_SIZE 512
1027
1028 static Id copydir_complex(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache);
1029
1030 static inline Id
1031 copydir(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache)
1032 {
1033   if (cache && cache[did & 255] == did)
1034     return cache[(did & 255) + 256];
1035   return copydir_complex(pool, data, fromspool, fromdata, did, cache);
1036 }
1037
1038 static Id
1039 copydir_complex(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache)
1040 {
1041   Id parent = dirpool_parent(&fromdata->dirpool, did);
1042   Id compid = dirpool_compid(&fromdata->dirpool, did);
1043   if (parent)
1044     parent = copydir(pool, data, fromspool, fromdata, parent, cache);
1045   if (fromspool != &pool->ss)
1046     compid = str2id(pool, stringpool_id2str(fromspool, compid), 1);
1047   compid = dirpool_add_dir(&data->dirpool, parent, compid, 1);
1048   if (cache)
1049     {
1050       cache[did & 255] = did;
1051       cache[(did & 255) + 256] = compid;
1052     }
1053   return compid;
1054 }
1055
1056 struct solvable_copy_cbdata {
1057   Repodata *data;
1058   Id handle;
1059   Id *dircache;
1060 };
1061
1062 static int
1063 solvable_copy_cb(void *vcbdata, Solvable *r, Repodata *fromdata, Repokey *key, KeyValue *kv)
1064 {
1065   struct solvable_copy_cbdata *cbdata = vcbdata;
1066   Id id, keyname;
1067   Repodata *data = cbdata->data;
1068   Id handle = cbdata->handle;
1069   Pool *pool = data->repo->pool, *frompool = fromdata->repo->pool;
1070   Stringpool *fromspool = fromdata->localpool ? &fromdata->spool : &frompool->ss;
1071
1072   keyname = key->name;
1073   if (keyname >= ID_NUM_INTERNAL)
1074     keyname = str2id(pool, id2str(frompool, keyname), 1);
1075   switch(key->type)
1076     {
1077     case REPOKEY_TYPE_ID:
1078     case REPOKEY_TYPE_CONSTANTID:
1079       id = kv->id;
1080       assert(!data->localpool); /* implement me! */
1081       if (pool != frompool || fromdata->localpool)
1082         {
1083           if (ISRELDEP(id))
1084             id = copyreldep(pool, frompool, id);
1085           else
1086             id = str2id(pool, stringpool_id2str(fromspool, id), 1);
1087         }
1088       if (key->type == REPOKEY_TYPE_ID)
1089         repodata_set_id(data, handle, keyname, id);
1090       else
1091         repodata_set_constantid(data, handle, keyname, id);
1092       break;
1093     case REPOKEY_TYPE_STR:
1094       repodata_set_str(data, handle, keyname, kv->str);
1095       break;
1096     case REPOKEY_TYPE_VOID:
1097       repodata_set_void(data, handle, keyname);
1098       break;
1099     case REPOKEY_TYPE_NUM:
1100       repodata_set_num(data, handle, keyname, kv->num);
1101       break;
1102     case REPOKEY_TYPE_CONSTANT:
1103       repodata_set_constant(data, handle, keyname, kv->num);
1104       break;
1105     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1106       id = kv->id;
1107       assert(!data->localpool); /* implement me! */
1108       id = copydir(pool, data, fromspool, fromdata, id, cbdata->dircache);
1109       repodata_add_dirnumnum(data, handle, keyname, id, kv->num, kv->num2);
1110       break;
1111     case REPOKEY_TYPE_DIRSTRARRAY:
1112       id = kv->id;
1113       assert(!data->localpool); /* implement me! */
1114       id = copydir(pool, data, fromspool, fromdata, id, cbdata->dircache);
1115       repodata_add_dirstr(data, handle, keyname, id, kv->str);
1116       break;
1117     default:
1118       break;
1119     }
1120   return 0;
1121 }
1122
1123 static void
1124 solvable_copy(Solvable *s, Solvable *r, Repodata *data, Id *dircache)
1125 {
1126   Repo *repo = s->repo;
1127   Repo *fromrepo = r->repo;
1128   Pool *pool = repo->pool;
1129   struct solvable_copy_cbdata cbdata;
1130
1131   /* copy solvable data */
1132   if (pool == fromrepo->pool)
1133     {
1134       s->name = r->name;
1135       s->evr = r->evr;
1136       s->arch = r->arch;
1137       s->vendor = r->vendor;
1138     }
1139   else
1140     {
1141       if (r->name)
1142         s->name = str2id(pool, id2str(fromrepo->pool, r->name), 1);
1143       if (r->evr)
1144         s->evr = str2id(pool, id2str(fromrepo->pool, r->evr), 1);
1145       if (r->arch)
1146         s->arch = str2id(pool, id2str(fromrepo->pool, r->arch), 1);
1147       if (r->vendor)
1148         s->vendor = str2id(pool, id2str(fromrepo->pool, r->vendor), 1);
1149     }
1150   s->provides = copydeps(pool, repo, r->provides, fromrepo);
1151   s->requires = copydeps(pool, repo, r->requires, fromrepo);
1152   s->conflicts = copydeps(pool, repo, r->conflicts, fromrepo);
1153   s->obsoletes = copydeps(pool, repo, r->obsoletes, fromrepo);
1154   s->recommends = copydeps(pool, repo, r->recommends, fromrepo);
1155   s->suggests = copydeps(pool, repo, r->suggests, fromrepo);
1156   s->supplements = copydeps(pool, repo, r->supplements, fromrepo);
1157   s->enhances  = copydeps(pool, repo, r->enhances, fromrepo);
1158
1159   /* copy all attributes */
1160   if (!data)
1161     return;
1162   cbdata.data = data;
1163   cbdata.handle = s - pool->solvables;
1164   cbdata.dircache = dircache;
1165   repo_search(fromrepo, (r - fromrepo->pool->solvables), 0, 0, SEARCH_NO_STORAGE_SOLVABLE, solvable_copy_cb, &cbdata);
1166 }
1167
1168 /* used to sort entries returned in some database order */
1169 static int
1170 rpmids_sort_cmp(const void *va, const void *vb, void *dp)
1171 {
1172   struct rpmid const *a = va, *b = vb;
1173   int r;
1174   r = strcmp(a->name, b->name);
1175   if (r)
1176     return r;
1177   return a->dbid - b->dbid;
1178 }
1179
1180 static int
1181 pkgids_sort_cmp(const void *va, const void *vb, void *dp)
1182 {
1183   Repo *repo = dp;
1184   Pool *pool = repo->pool;
1185   Solvable *a = pool->solvables + *(Id *)va;
1186   Solvable *b = pool->solvables + *(Id *)vb;
1187   Id *rpmdbid;
1188
1189   if (a->name != b->name)
1190     return strcmp(id2str(pool, a->name), id2str(pool, b->name));
1191   rpmdbid = repo->rpmdbid;
1192   return rpmdbid[(a - pool->solvables) - repo->start] - rpmdbid[(b - pool->solvables) - repo->start];
1193 }
1194
1195 static void
1196 swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
1197 {
1198   Pool *pool = repo->pool;
1199   Solvable tmp;
1200
1201   tmp = pool->solvables[pa];
1202   pool->solvables[pa] = pool->solvables[pb];
1203   pool->solvables[pb] = tmp;
1204   if (repo->rpmdbid)
1205     {
1206       Id tmpid = repo->rpmdbid[pa - repo->start];
1207       repo->rpmdbid[pa - repo->start] = repo->rpmdbid[pb - repo->start];
1208       repo->rpmdbid[pb - repo->start] = tmpid;
1209     }
1210   /* only works if nothing is already internalized! */
1211   if (data && data->attrs)
1212     {
1213       Id *tmpattrs = data->attrs[pa - data->start];
1214       data->attrs[pa - data->start] = data->attrs[pb - data->start];
1215       data->attrs[pb - data->start] = tmpattrs;
1216     }
1217 }
1218
1219 static void
1220 mkrpmdbcookie(struct stat *st, unsigned char *cookie)
1221 {
1222   memset(cookie, 0, 32);
1223   cookie[3] = RPMDB_COOKIE_VERSION;
1224   memcpy(cookie + 16, &st->st_ino, sizeof(st->st_ino));
1225   memcpy(cookie + 24, &st->st_dev, sizeof(st->st_dev));
1226 }
1227
1228 /* should look in /usr/lib/rpm/macros instead, but we want speed... */
1229 static DB_ENV *
1230 opendbenv(const char *rootdir)
1231 {
1232   char dbpath[PATH_MAX];
1233   DB_ENV *dbenv = 0;
1234   int r;
1235
1236   if (db_env_create(&dbenv, 0))
1237     {
1238       perror("db_env_create");
1239       return 0;
1240     }
1241 #if defined(FEDORA) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
1242   dbenv->set_thread_count(dbenv, 8);
1243 #endif
1244   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : "");
1245   if (access(dbpath, W_OK) == -1)
1246     {
1247       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
1248     }
1249   else
1250     {
1251 #ifdef FEDORA
1252       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0644);
1253 #else
1254       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
1255 #endif
1256     }
1257   if (r)
1258     {
1259       perror("dbenv open");
1260       dbenv->close(dbenv, 0);
1261       return 0;
1262     }
1263   return dbenv;
1264 }
1265
1266
1267 static int
1268 count_headers(const char *rootdir, DB_ENV *dbenv)
1269 {
1270   char dbpath[PATH_MAX];
1271   struct stat statbuf;
1272   int byteswapped;
1273   DB *db = 0;
1274   DBC *dbc = 0;
1275   int count = 0;
1276   DBT dbkey;
1277   DBT dbdata;
1278
1279   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir);
1280   if (stat(dbpath, &statbuf))
1281     return 0;
1282   memset(&dbkey, 0, sizeof(dbkey));
1283   memset(&dbdata, 0, sizeof(dbdata));
1284   if (db_create(&db, dbenv, 0))
1285     {
1286       perror("db_create");
1287       exit(1);
1288     }
1289   if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1290     {
1291       perror("db->open Name index");
1292       exit(1);
1293     }
1294   if (db->get_byteswapped(db, &byteswapped))
1295     {
1296       perror("db->get_byteswapped");
1297       exit(1);
1298     }
1299   if (db->cursor(db, NULL, &dbc, 0))
1300     {
1301       perror("db->cursor");
1302       exit(1);
1303     }
1304   while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1305     count += dbdata.size >> 3;
1306   dbc->c_close(dbc);
1307   db->close(db, 0);
1308   return count;
1309 }
1310
1311 /*
1312  * read rpm db as repo
1313  *
1314  */
1315
1316 void
1317 repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir, int flags)
1318 {
1319   Pool *pool = repo->pool;
1320   unsigned char buf[16];
1321   DB *db = 0;
1322   DBC *dbc = 0;
1323   int byteswapped;
1324   unsigned int dbid;
1325   unsigned char *dp, *dbidp;
1326   int dl, nrpmids;
1327   struct rpmid *rpmids, *rp;
1328   int i;
1329   int rpmheadsize;
1330   RpmHead *rpmhead;
1331   Solvable *s;
1332   Id id, *refhash;
1333   unsigned int refmask, h;
1334   char dbpath[PATH_MAX];
1335   DB_ENV *dbenv = 0;
1336   DBT dbkey;
1337   DBT dbdata;
1338   struct stat packagesstat;
1339   unsigned char newcookie[32];
1340   const unsigned char *oldcookie = 0;
1341   Id oldcookietype = 0;
1342   Repodata *data;
1343   int count = 0, done = 0;
1344   unsigned int now;
1345
1346   now = sat_timems(0);
1347   memset(&dbkey, 0, sizeof(dbkey));
1348   memset(&dbdata, 0, sizeof(dbdata));
1349
1350   if (!rootdir)
1351     rootdir = "";
1352
1353   data = repo_add_repodata(repo, flags);
1354
1355   if (ref && !(ref->nsolvables && ref->rpmdbid))
1356     ref = 0;
1357
1358   if (!(dbenv = opendbenv(rootdir)))
1359     exit(1);
1360
1361   /* XXX: should get ro lock of Packages database! */
1362   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
1363   if (stat(dbpath, &packagesstat))
1364     {
1365       perror(dbpath);
1366       exit(1);
1367     }
1368   mkrpmdbcookie(&packagesstat, newcookie);
1369   repodata_set_bin_checksum(data, SOLVID_META, REPOSITORY_RPMDBCOOKIE, REPOKEY_TYPE_SHA256, newcookie);
1370
1371   if (ref)
1372     oldcookie = repo_lookup_bin_checksum(ref, SOLVID_META, REPOSITORY_RPMDBCOOKIE, &oldcookietype);
1373   if (!ref || !oldcookie || oldcookietype != REPOKEY_TYPE_SHA256 || memcmp(oldcookie, newcookie, 32) != 0)
1374     {
1375       Id *pkgids;
1376       int solvstart = 0, solvend = 0;
1377
1378       if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1379         count = count_headers(rootdir, dbenv);
1380       if (db_create(&db, dbenv, 0))
1381         {
1382           perror("db_create");
1383           exit(1);
1384         }
1385       if (db->open(db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1386         {
1387           perror("db->open Packages index");
1388           exit(1);
1389         }
1390       if (db->get_byteswapped(db, &byteswapped))
1391         {
1392           perror("db->get_byteswapped");
1393           exit(1);
1394         }
1395       if (db->cursor(db, NULL, &dbc, 0))
1396         {
1397           perror("db->cursor");
1398           exit(1);
1399         }
1400       dbidp = (unsigned char *)&dbid;
1401       rpmheadsize = 0;
1402       rpmhead = 0;
1403       i = 0;
1404       s = 0;
1405       while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1406         {
1407           if (!s)
1408             {
1409               s = pool_id2solvable(pool, repo_add_solvable(repo));
1410               if (!solvstart)
1411                 solvstart = s - pool->solvables;
1412               solvend = s - pool->solvables + 1;
1413             }
1414           if (!repo->rpmdbid)
1415             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1416           if (dbkey.size != 4)
1417             {
1418               fprintf(stderr, "corrupt Packages database (key size)\n");
1419               exit(1);
1420             }
1421           dp = dbkey.data;
1422           if (byteswapped)
1423             {
1424               dbidp[0] = dp[3];
1425               dbidp[1] = dp[2];
1426               dbidp[2] = dp[1];
1427               dbidp[3] = dp[0];
1428             }
1429           else
1430             memcpy(dbidp, dp, 4);
1431           if (dbid == 0)                /* the join key */
1432             continue;
1433           if (dbdata.size < 8)
1434             {
1435               fprintf(stderr, "corrupt rpm database (size %u)\n", dbdata.size);
1436               exit(1);
1437             }
1438           if (dbdata.size > rpmheadsize)
1439             {
1440               rpmheadsize = dbdata.size + 128;
1441               rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
1442             }
1443           memcpy(buf, dbdata.data, 8);
1444           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1445           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1446           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1447             {
1448               fprintf(stderr, "corrupt rpm database (data size)\n");
1449               exit(1);
1450             }
1451           memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1452           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1453           repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
1454           if (rpm2solv(pool, repo, data, s, rpmhead, flags | RPM_ADD_TRIGGERS))
1455             {
1456               i++;
1457               s = 0;
1458             }
1459           else
1460             {
1461               /* We can reuse this solvable, but make sure it's still
1462                  associated with this repo.  */
1463               memset(s, 0, sizeof(*s));
1464               s->repo = repo;
1465             }
1466           if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1467             {
1468               if (done < count)
1469                 done++;
1470               if (done < count && (done - 1) * 100 / count != done * 100 / count)
1471                 pool_debug(pool, SAT_ERROR, "%%%% %d\n", done * 100 / count);
1472             }
1473         }
1474       if (s)
1475         {
1476           /* oops, could not reuse. free it instead */
1477           repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
1478           solvend--;
1479           s = 0;
1480         }
1481       dbc->c_close(dbc);
1482       db->close(db, 0);
1483       db = 0;
1484       /* now sort all solvables in the new solvstart..solvend block */
1485       if (solvend - solvstart > 1)
1486         {
1487           pkgids = sat_malloc2(solvend - solvstart, sizeof(Id));
1488           for (i = solvstart; i < solvend; i++)
1489             pkgids[i - solvstart] = i;
1490           sat_sort(pkgids, solvend - solvstart, sizeof(Id), pkgids_sort_cmp, repo);
1491           /* adapt order */
1492           for (i = solvstart; i < solvend; i++)
1493             {
1494               int j = pkgids[i - solvstart];
1495               while (j < i)
1496                 j = pkgids[i - solvstart] = pkgids[j - solvstart];
1497               if (j != i)
1498                 swap_solvables(repo, data, i, j);
1499             }
1500           sat_free(pkgids);
1501         }
1502     }
1503   else
1504     {
1505       Id dircache[COPYDIR_DIRCACHE_SIZE];               /* see copydir */
1506
1507       memset(dircache, 0, sizeof(dircache));
1508       if (db_create(&db, dbenv, 0))
1509         {
1510           perror("db_create");
1511           exit(1);
1512         }
1513       if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1514         {
1515           perror("db->open Name index");
1516           exit(1);
1517         }
1518       if (db->get_byteswapped(db, &byteswapped))
1519         {
1520           perror("db->get_byteswapped");
1521           exit(1);
1522         }
1523       if (db->cursor(db, NULL, &dbc, 0))
1524         {
1525           perror("db->cursor");
1526           exit(1);
1527         }
1528       dbidp = (unsigned char *)&dbid;
1529       nrpmids = 0;
1530       rpmids = 0;
1531       while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1532         {
1533           if (dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
1534             continue;
1535           dl = dbdata.size;
1536           dp = dbdata.data;
1537           while(dl >= 8)
1538             {
1539               if (byteswapped)
1540                 {
1541                   dbidp[0] = dp[3];
1542                   dbidp[1] = dp[2];
1543                   dbidp[2] = dp[1];
1544                   dbidp[3] = dp[0];
1545                 }
1546               else
1547                 memcpy(dbidp, dp, 4);
1548               rpmids = sat_extend(rpmids, nrpmids, 1, sizeof(*rpmids), 255);
1549               rpmids[nrpmids].dbid = dbid;
1550               rpmids[nrpmids].name = sat_malloc((int)dbkey.size + 1);
1551               memcpy(rpmids[nrpmids].name, dbkey.data, (int)dbkey.size);
1552               rpmids[nrpmids].name[(int)dbkey.size] = 0;
1553               nrpmids++;
1554               dp += 8;
1555               dl -= 8;
1556             }
1557         }
1558       dbc->c_close(dbc);
1559       db->close(db, 0);
1560       db = 0;
1561
1562       /* sort rpmids */
1563       sat_sort(rpmids, nrpmids, sizeof(*rpmids), rpmids_sort_cmp, 0);
1564
1565       dbidp = (unsigned char *)&dbid;
1566       rpmheadsize = 0;
1567       rpmhead = 0;
1568
1569       /* create hash from dbid to ref */
1570       refmask = mkmask(ref->nsolvables);
1571       refhash = sat_calloc(refmask + 1, sizeof(Id));
1572       for (i = 0; i < ref->end - ref->start; i++)
1573         {
1574           if (!ref->rpmdbid[i])
1575             continue;
1576           h = ref->rpmdbid[i] & refmask;
1577           while (refhash[h])
1578             h = (h + 317) & refmask;
1579           refhash[h] = i + 1;   /* make it non-zero */
1580         }
1581
1582       /* count the misses, they will cost us time */
1583       if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1584         {
1585           for (i = 0, rp = rpmids; i < nrpmids; i++, rp++)
1586             {
1587               dbid = rp->dbid;
1588               if (refhash)
1589                 {
1590                   h = dbid & refmask;
1591                   while ((id = refhash[h]))
1592                     {
1593                       if (ref->rpmdbid[id - 1] == dbid)
1594                         break;
1595                       h = (h + 317) & refmask;
1596                     }
1597                   if (id)
1598                     continue;
1599                 }
1600               count++;
1601             }
1602         }
1603
1604       s = pool_id2solvable(pool, repo_add_solvable_block(repo, nrpmids));
1605       if (!repo->rpmdbid)
1606         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1607
1608       for (i = 0, rp = rpmids; i < nrpmids; i++, rp++, s++)
1609         {
1610           dbid = rp->dbid;
1611           repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
1612           if (refhash)
1613             {
1614               h = dbid & refmask;
1615               while ((id = refhash[h]))
1616                 {
1617                   if (ref->rpmdbid[id - 1] == dbid)
1618                     break;
1619                   h = (h + 317) & refmask;
1620                 }
1621               if (id)
1622                 {
1623                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
1624                   if (r->repo == ref)
1625                     {
1626                       solvable_copy(s, r, data, dircache);
1627                       continue;
1628                     }
1629                 }
1630             }
1631           if (!db)
1632             {
1633               if (db_create(&db, dbenv, 0))
1634                 {
1635                   perror("db_create");
1636                   exit(1);
1637                 }
1638               if (db->open(db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1639                 {
1640                   perror("db->open var/lib/rpm/Packages");
1641                   exit(1);
1642                 }
1643               if (db->get_byteswapped(db, &byteswapped))
1644                 {
1645                   perror("db->get_byteswapped");
1646                   exit(1);
1647                 }
1648             }
1649           if (byteswapped)
1650             {
1651               buf[0] = dbidp[3];
1652               buf[1] = dbidp[2];
1653               buf[2] = dbidp[1];
1654               buf[3] = dbidp[0];
1655             }
1656           else
1657             memcpy(buf, dbidp, 4);
1658           dbkey.data = buf;
1659           dbkey.size = 4;
1660           dbdata.data = 0;
1661           dbdata.size = 0;
1662           if (db->get(db, NULL, &dbkey, &dbdata, 0))
1663             {
1664               perror("db->get");
1665               fprintf(stderr, "corrupt rpm database, key %d not found\n", dbid);
1666               fprintf(stderr, "please run 'rpm --rebuilddb' to recreate the database index files\n");
1667               exit(1);
1668             }
1669           if (dbdata.size < 8)
1670             {
1671               fprintf(stderr, "corrupt rpm database (size)\n");
1672               exit(1);
1673             }
1674           if (dbdata.size > rpmheadsize)
1675             {
1676               rpmheadsize = dbdata.size + 128;
1677               rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
1678             }
1679           memcpy(buf, dbdata.data, 8);
1680           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1681           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1682           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1683             {
1684               fprintf(stderr, "corrupt rpm database (data size)\n");
1685               exit(1);
1686             }
1687           memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1688           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1689
1690           rpm2solv(pool, repo, data, s, rpmhead, flags | RPM_ADD_TRIGGERS);
1691           if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1692             {
1693               if (done < count)
1694                 done++;
1695               if (done < count && (done - 1) * 100 / count != done * 100 / count)
1696                 pool_debug(pool, SAT_ERROR, "%%%% %d\n", done * 100 / count);
1697             }
1698         }
1699
1700       if (refhash)
1701         sat_free(refhash);
1702       if (rpmids)
1703         {
1704           for (i = 0; i < nrpmids; i++)
1705             sat_free(rpmids[i].name);
1706           sat_free(rpmids);
1707         }
1708     }
1709   if (db)
1710     db->close(db, 0);
1711   dbenv->close(dbenv, 0);
1712   if (rpmhead)
1713     sat_free(rpmhead);
1714   if (!(flags & REPO_NO_INTERNALIZE))
1715     repodata_internalize(data);
1716   if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1717     pool_debug(pool, SAT_ERROR, "%%%% 100\n");
1718   POOL_DEBUG(SAT_DEBUG_STATS, "repo_add_rpmdb took %d ms\n", sat_timems(now));
1719   POOL_DEBUG(SAT_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
1720   POOL_DEBUG(SAT_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", data->incoredatalen/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
1721 }
1722
1723
1724 static inline unsigned int
1725 getu32(const unsigned char *dp)
1726 {
1727   return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
1728 }
1729
1730
1731 void
1732 repo_add_rpms(Repo *repo, const char **rpms, int nrpms, int flags)
1733 {
1734   int i, sigdsize, sigcnt, l;
1735   Pool *pool = repo->pool;
1736   Solvable *s;
1737   RpmHead *rpmhead = 0;
1738   int rpmheadsize = 0;
1739   char *payloadformat;
1740   FILE *fp;
1741   unsigned char lead[4096];
1742   int headerstart, headerend;
1743   struct stat stb;
1744   Repodata *data;
1745   unsigned char pkgid[16];
1746   int gotpkgid;
1747   Id chksumtype = 0;
1748   void *chksumh = 0;
1749
1750   data = repo_add_repodata(repo, flags);
1751
1752   if ((flags & RPM_ADD_WITH_SHA256SUM) != 0)
1753     chksumtype = REPOKEY_TYPE_SHA256;
1754   else if ((flags & RPM_ADD_WITH_SHA1SUM) != 0)
1755     chksumtype = REPOKEY_TYPE_SHA1;
1756   for (i = 0; i < nrpms; i++)
1757     {
1758       if ((fp = fopen(rpms[i], "r")) == 0)
1759         {
1760           perror(rpms[i]);
1761           continue;
1762         }
1763       if (fstat(fileno(fp), &stb))
1764         {
1765           perror("stat");
1766           continue;
1767         }
1768       if (chksumh)
1769         chksumh = sat_chksum_free(chksumh, 0);
1770       if (chksumtype)
1771         chksumh = sat_chksum_create(chksumtype);
1772       if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
1773         {
1774           fprintf(stderr, "%s: not a rpm\n", rpms[i]);
1775           fclose(fp);
1776           continue;
1777         }
1778       if (chksumh)
1779         sat_chksum_add(chksumh, lead, 96 + 16);
1780       if (lead[78] != 0 || lead[79] != 5)
1781         {
1782           fprintf(stderr, "%s: not a V5 header\n", rpms[i]);
1783           fclose(fp);
1784           continue;
1785         }
1786       if (getu32(lead + 96) != 0x8eade801)
1787         {
1788           fprintf(stderr, "%s: bad signature header\n", rpms[i]);
1789           fclose(fp);
1790           continue;
1791         }
1792       sigcnt = getu32(lead + 96 + 8);
1793       sigdsize = getu32(lead + 96 + 12);
1794       if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
1795         {
1796           fprintf(stderr, "%s: bad signature header\n", rpms[i]);
1797           fclose(fp);
1798           continue;
1799         }
1800       sigdsize += sigcnt * 16;
1801       sigdsize = (sigdsize + 7) & ~7;
1802       headerstart = 96 + 16 + sigdsize;
1803       gotpkgid = 0;
1804       if ((flags & RPM_ADD_WITH_PKGID) != 0)
1805         {
1806           unsigned char *chksum;
1807           unsigned int chksumsize;
1808           /* extract pkgid from the signature header */
1809           if (sigdsize > rpmheadsize)
1810             {
1811               rpmheadsize = sigdsize + 128;
1812               rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
1813             }
1814           if (fread(rpmhead->data, sigdsize, 1, fp) != 1)
1815             {
1816               fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1817               fclose(fp);
1818               continue;
1819             }
1820           if (chksumh)
1821             sat_chksum_add(chksumh, rpmhead->data, sigdsize);
1822           rpmhead->cnt = sigcnt;
1823           rpmhead->dcnt = sigdsize - sigcnt * 16;
1824           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1825           chksum = headbinary(rpmhead, SIGTAG_MD5, &chksumsize);
1826           if (chksum && chksumsize == 16)
1827             {
1828               gotpkgid = 1;
1829               memcpy(pkgid, chksum, 16);
1830             }
1831         }
1832       else
1833         {
1834           /* just skip the signature header */
1835           while (sigdsize)
1836             {
1837               l = sigdsize > 4096 ? 4096 : sigdsize;
1838               if (fread(lead, l, 1, fp) != 1)
1839                 {
1840                   fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1841                   fclose(fp);
1842                   continue;
1843                 }
1844               if (chksumh)
1845                 sat_chksum_add(chksumh, lead, l);
1846               sigdsize -= l;
1847             }
1848         }
1849       if (fread(lead, 16, 1, fp) != 1)
1850         {
1851           fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1852           fclose(fp);
1853           continue;
1854         }
1855       if (chksumh)
1856         sat_chksum_add(chksumh, lead, 16);
1857       if (getu32(lead) != 0x8eade801)
1858         {
1859           fprintf(stderr, "%s: bad header\n", rpms[i]);
1860           fclose(fp);
1861           continue;
1862         }
1863       sigcnt = getu32(lead + 8);
1864       sigdsize = getu32(lead + 12);
1865       if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
1866         {
1867           fprintf(stderr, "%s: bad header\n", rpms[i]);
1868           fclose(fp);
1869           continue;
1870         }
1871       l = sigdsize + sigcnt * 16;
1872       headerend = headerstart + 16 + l;
1873       if (l > rpmheadsize)
1874         {
1875           rpmheadsize = l + 128;
1876           rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
1877         }
1878       if (fread(rpmhead->data, l, 1, fp) != 1)
1879         {
1880           fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1881           fclose(fp);
1882           continue;
1883         }
1884       if (chksumh)
1885         sat_chksum_add(chksumh, rpmhead->data, l);
1886       rpmhead->cnt = sigcnt;
1887       rpmhead->dcnt = sigdsize;
1888       rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1889       if (headexists(rpmhead, TAG_PATCHESNAME))
1890         {
1891           /* this is a patch rpm, ignore */
1892           fclose(fp);
1893           continue;
1894         }
1895       payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
1896       if (payloadformat && !strcmp(payloadformat, "drpm"))
1897         {
1898           /* this is a delta rpm */
1899           fclose(fp);
1900           continue;
1901         }
1902       if (chksumh)
1903         while ((l = fread(lead, 1, sizeof(lead), fp)) > 0)
1904           sat_chksum_add(chksumh, lead, l);
1905       fclose(fp);
1906       s = pool_id2solvable(pool, repo_add_solvable(repo));
1907       rpm2solv(pool, repo, data, s, rpmhead, flags);
1908       if (data)
1909         {
1910           Id handle = s - pool->solvables;
1911           repodata_set_location(data, handle, 0, 0, rpms[i]);
1912           if (S_ISREG(stb.st_mode))
1913             repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)((stb.st_size + 1023) / 1024));
1914           repodata_set_num(data, handle, SOLVABLE_HEADEREND, headerend);
1915           if (gotpkgid)
1916             repodata_set_bin_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
1917           if (chksumh)
1918             repodata_set_bin_checksum(data, handle, SOLVABLE_CHECKSUM, chksumtype, sat_chksum_get(chksumh, 0));
1919         }
1920     }
1921   if (chksumh)
1922     chksumh = sat_chksum_free(chksumh, 0);
1923   if (rpmhead)
1924     sat_free(rpmhead);
1925   if (!(flags & REPO_NO_INTERNALIZE))
1926     repodata_internalize(data);
1927 }
1928
1929 static inline void
1930 linkhash(const char *lt, char *hash)
1931 {
1932   unsigned int r = 0;
1933   const unsigned char *str = (const unsigned char *)lt;
1934   int l, c;
1935
1936   l = strlen(lt);
1937   while ((c = *str++) != 0)
1938     r += (r << 3) + c;
1939   sprintf(hash, "%08x", r);
1940   sprintf(hash + 8, "%08x", l);
1941   sprintf(hash + 16, "%08x", 0);
1942   sprintf(hash + 24, "%08x", 0);
1943 }
1944
1945 void
1946 rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, int, const char *), void *cbdata)
1947 {
1948   RpmHead *rpmhead = rpmhandle;
1949   char **bn;
1950   char **dn;
1951   char **md = 0;
1952   char **lt = 0;
1953   unsigned int *di, diidx;
1954   unsigned int *co = 0;
1955   unsigned int lastdir;
1956   int lastdirl;
1957   unsigned int *fm;
1958   int cnt, dcnt, cnt2;
1959   int i, l1, l;
1960   char *space = 0;
1961   int spacen = 0;
1962   char md5[33], *md5p = 0;
1963
1964   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dcnt);
1965   if (!dn)
1966     return;
1967   if ((flags & RPM_ITERATE_FILELIST_ONLYDIRS) != 0)
1968     {
1969       for (i = 0; i < dcnt; i++)
1970         (*cb)(cbdata, dn[i], 0, (char *)0);
1971       sat_free(dn);
1972       return;
1973     }
1974   bn = headstringarray(rpmhead, TAG_BASENAMES, &cnt);
1975   if (!bn)
1976     {
1977       sat_free(dn);
1978       return;
1979     }
1980   di = headint32array(rpmhead, TAG_DIRINDEXES, &cnt2);
1981   if (!di || cnt != cnt2)
1982     {
1983       sat_free(di);
1984       sat_free(bn);
1985       sat_free(dn);
1986       return;
1987     }
1988   fm = headint16array(rpmhead, TAG_FILEMODES, &cnt2);
1989   if (!fm || cnt != cnt2)
1990     {
1991       sat_free(fm);
1992       sat_free(di);
1993       sat_free(bn);
1994       sat_free(dn);
1995       return;
1996     }
1997   if ((flags & RPM_ITERATE_FILELIST_WITHMD5) != 0)
1998     {
1999       md = headstringarray(rpmhead, TAG_FILEMD5S, &cnt2);
2000       if (!md || cnt != cnt2)
2001         {
2002           sat_free(md);
2003           sat_free(fm);
2004           sat_free(di);
2005           sat_free(bn);
2006           sat_free(dn);
2007           return;
2008         }
2009     }
2010   if ((flags & RPM_ITERATE_FILELIST_WITHCOL) != 0)
2011     {
2012       co = headint32array(rpmhead, TAG_FILECOLORS, &cnt2);
2013       if (!co || cnt != cnt2)
2014         {
2015           sat_free(co);
2016           sat_free(md);
2017           sat_free(fm);
2018           sat_free(di);
2019           sat_free(bn);
2020           sat_free(dn);
2021           return;
2022         }
2023     }
2024   lastdir = dcnt;
2025   lastdirl = 0;
2026   for (i = 0; i < cnt; i++)
2027     {
2028       diidx = di[i];
2029       if (diidx >= dcnt)
2030         continue;
2031       l1 = lastdir == diidx ? lastdirl : strlen(dn[diidx]);
2032       if (l1 == 0)
2033         continue;
2034       l = l1 + strlen(bn[i]) + 1;
2035       if (l > spacen)
2036         {
2037           spacen = l + 16;
2038           space = sat_realloc(space, spacen);
2039         }
2040       if (lastdir != diidx)
2041         {
2042           strcpy(space, dn[diidx]);
2043           lastdir = diidx;
2044           lastdirl = l1;
2045         }
2046       strcpy(space + l1, bn[i]);
2047       if (md)
2048         {
2049           md5p = md[i];
2050           if (S_ISLNK(fm[i]))
2051             {
2052               md5p = 0;
2053               if (!lt)
2054                 {
2055                   lt = headstringarray(rpmhead, TAG_FILELINKTOS, &cnt2);
2056                   if (cnt != cnt2)
2057                     lt = sat_free(lt);
2058                 }
2059               if (lt)
2060                 {
2061                   linkhash(lt[i], md5);
2062                   md5p = md5;
2063                 }
2064             }
2065           if (!md5p)
2066             {
2067               sprintf(md5, "%08x%08x", (fm[i] >> 12) & 65535, 0);
2068               md5p = md5;
2069             }
2070         }
2071       (*cb)(cbdata, space, co ? (fm[i] | co[i] << 24) : fm[i], md5p);
2072     }
2073   sat_free(space);
2074   sat_free(lt);
2075   sat_free(md);
2076   sat_free(fm);
2077   sat_free(di);
2078   sat_free(bn);
2079   sat_free(dn);
2080   sat_free(co);
2081 }
2082
2083 char *
2084 rpm_query(void *rpmhandle, Id what)
2085 {
2086   const char *name, *arch, *sourcerpm;
2087   char *evr, *r;
2088   int l;
2089
2090   RpmHead *rpmhead = rpmhandle;
2091   r = 0;
2092   switch (what)
2093     {
2094     case 0:
2095       name = headstring(rpmhead, TAG_NAME);
2096       if (!name)
2097         name = "";
2098       sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
2099       if (sourcerpm)
2100         arch = headstring(rpmhead, TAG_ARCH);
2101       else
2102         {
2103           if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
2104             arch = "nosrc";
2105           else
2106             arch = "src";
2107         }
2108       if (!arch)
2109         arch = "noarch";
2110       evr = headtoevr(rpmhead);
2111       l = strlen(name) + 1 + strlen(evr) + 1 + strlen(arch) + 1;
2112       r = sat_malloc(l);
2113       sprintf(r, "%s-%s.%s", name, evr, arch);
2114       free(evr);
2115       break;
2116     case SOLVABLE_NAME:
2117       name = headstring(rpmhead, TAG_NAME);
2118       r = strdup(name);
2119       break;
2120     case SOLVABLE_EVR:
2121       r = headtoevr(rpmhead);
2122       break;
2123     }
2124   return r;
2125 }
2126
2127
2128 struct rpm_by_state {
2129   RpmHead *rpmhead;
2130   int rpmheadsize;
2131
2132   int dbopened;
2133   DB_ENV *dbenv;
2134   DB *db;
2135   int byteswapped;
2136 };
2137
2138 struct rpmdbentry {
2139   Id rpmdbid;
2140   Id nameoff;
2141 };
2142
2143 #define ENTRIES_BLOCK 255
2144 #define NAMEDATA_BLOCK 1023
2145
2146 static struct rpmdbentry *
2147 getinstalledrpmdbids(struct rpm_by_state *state, const char *index, const char *match, int *nentriesp, char **namedatap)
2148 {
2149   DB_ENV *dbenv = 0;
2150   DB *db = 0;
2151   DBC *dbc = 0;
2152   int byteswapped;
2153   DBT dbkey;
2154   DBT dbdata;
2155   Id rpmdbid;
2156   unsigned char *dp;
2157   int dl;
2158
2159   char *namedata = 0;
2160   int namedatal = 0;
2161   struct rpmdbentry *entries = 0;
2162   int nentries = 0;
2163
2164   *nentriesp = 0;
2165   *namedatap = 0;
2166
2167   dbenv = state->dbenv;
2168   if (db_create(&db, dbenv, 0))
2169     {
2170       perror("db_create");
2171       return 0;
2172     }
2173   if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
2174     {
2175       perror("db->open index");
2176       db->close(db, 0);
2177       return 0;
2178     }
2179   if (db->get_byteswapped(db, &byteswapped))
2180     {
2181       perror("db->get_byteswapped");
2182       db->close(db, 0);
2183       return 0;
2184     }
2185   if (db->cursor(db, NULL, &dbc, 0))
2186     {
2187       perror("db->cursor");
2188       db->close(db, 0);
2189       return 0;
2190     }
2191   memset(&dbkey, 0, sizeof(dbkey));
2192   memset(&dbdata, 0, sizeof(dbdata));
2193   if (match)
2194     {
2195       dbkey.data = (void *)match;
2196       dbkey.size = strlen(match);
2197     }
2198   while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
2199     {
2200       if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
2201         continue;
2202       dl = dbdata.size;
2203       dp = dbdata.data;
2204       while(dl >= 8)
2205         {
2206           if (byteswapped)
2207             {
2208               ((char *)&rpmdbid)[0] = dp[3];
2209               ((char *)&rpmdbid)[1] = dp[2];
2210               ((char *)&rpmdbid)[2] = dp[1];
2211               ((char *)&rpmdbid)[3] = dp[0];
2212             }
2213           else
2214             memcpy((char *)&rpmdbid, dp, 4);
2215           entries = sat_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
2216           entries[nentries].rpmdbid = rpmdbid;
2217           entries[nentries].nameoff = namedatal;
2218           nentries++;
2219           namedata = sat_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
2220           memcpy(namedata + namedatal, dbkey.data, dbkey.size);
2221           namedata[namedatal + dbkey.size] = 0;
2222           namedatal += dbkey.size + 1;
2223           dp += 8;
2224           dl -= 8;
2225         }
2226       if (match)
2227         break;
2228     }
2229   dbc->c_close(dbc);
2230   db->close(db, 0);
2231   *nentriesp = nentries;
2232   *namedatap = namedata;
2233   return entries;
2234 }
2235
2236 static void
2237 freestate(struct rpm_by_state *state)
2238 {
2239   /* close down */
2240   if (!state)
2241     return;
2242   if (state->db)
2243     state->db->close(state->db, 0);
2244   if (state->dbenv)
2245     state->dbenv->close(state->dbenv, 0);
2246   sat_free(state->rpmhead);
2247 }
2248
2249 int
2250 rpm_installedrpmdbids(const char *rootdir, const char *index, const char *match, Queue *rpmdbidq)
2251 {
2252   struct rpm_by_state state;
2253   struct rpmdbentry *entries;
2254   int nentries, i;
2255   char *namedata;
2256
2257   if (!index)
2258     index = "Name";
2259   if (rpmdbidq)
2260     queue_empty(rpmdbidq);
2261   memset(&state, 0, sizeof(state));
2262   if (!(state.dbenv = opendbenv(rootdir)))
2263     return 0;
2264   entries = getinstalledrpmdbids(&state, index, match, &nentries, &namedata);
2265   if (rpmdbidq)
2266     for (i = 0; i < nentries; i++)
2267       queue_push(rpmdbidq, entries[i].rpmdbid);
2268   sat_free(entries);
2269   sat_free(namedata);
2270   freestate(&state);
2271   return nentries;
2272 }
2273
2274 void *
2275 rpm_byrpmdbid(Id rpmdbid, const char *rootdir, void **statep)
2276 {
2277   struct rpm_by_state *state = *statep;
2278   unsigned char buf[16];
2279   DBT dbkey;
2280   DBT dbdata;
2281   RpmHead *rpmhead;
2282
2283   if (!rpmdbid)
2284     {
2285       /* close down */
2286       freestate(state);
2287       sat_free(state);
2288       *statep = (void *)0;
2289       return 0;
2290     }
2291
2292   if (!state)
2293     {
2294       state = sat_calloc(1, sizeof(*state));
2295       *statep = state;
2296     }
2297   if (!state->dbopened)
2298     {
2299       state->dbopened = 1;
2300       if (!state->dbenv && !(state->dbenv = opendbenv(rootdir)))
2301         return 0;
2302       if (db_create(&state->db, state->dbenv, 0))
2303         {
2304           perror("db_create");
2305           state->db = 0;
2306           state->dbenv->close(state->dbenv, 0);
2307           state->dbenv = 0;
2308           return 0;
2309         }
2310       if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
2311         {
2312           perror("db->open var/lib/rpm/Packages");
2313           state->db->close(state->db, 0);
2314           state->db = 0;
2315           state->dbenv->close(state->dbenv, 0);
2316           state->dbenv = 0;
2317           return 0;
2318         }
2319       if (state->db->get_byteswapped(state->db, &state->byteswapped))
2320         {
2321           perror("db->get_byteswapped");
2322           state->db->close(state->db, 0);
2323           state->db = 0;
2324           state->dbenv->close(state->dbenv, 0);
2325           state->dbenv = 0;
2326           return 0;
2327         }
2328     }
2329   memcpy(buf, &rpmdbid, 4);
2330   if (state->byteswapped)
2331     {
2332       unsigned char bx;
2333       bx = buf[0]; buf[0] = buf[3]; buf[3] = bx;
2334       bx = buf[1]; buf[1] = buf[2]; buf[2] = bx;
2335     }
2336   memset(&dbkey, 0, sizeof(dbkey));
2337   memset(&dbdata, 0, sizeof(dbdata));
2338   dbkey.data = buf;
2339   dbkey.size = 4;
2340   dbdata.data = 0;
2341   dbdata.size = 0;
2342   if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
2343     {
2344       perror("db->get");
2345       return 0;
2346     }
2347   if (dbdata.size < 8)
2348     {
2349       fprintf(stderr, "corrupt rpm database (size)\n");
2350       return 0;
2351     }
2352   if (dbdata.size > state->rpmheadsize)
2353     {
2354       state->rpmheadsize = dbdata.size + 128;
2355       state->rpmhead = sat_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
2356     }
2357   rpmhead = state->rpmhead;
2358   memcpy(buf, dbdata.data, 8);
2359   rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
2360   rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
2361   if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
2362     {
2363       fprintf(stderr, "corrupt rpm database (data size)\n");
2364       return 0;
2365     }
2366   memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
2367   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2368   return rpmhead;
2369 }
2370
2371 void *
2372 rpm_byfp(FILE *fp, const char *name, void **statep)
2373 {
2374   struct rpm_by_state *state = *statep;
2375   int headerstart, headerend;
2376   RpmHead *rpmhead;
2377   int sigdsize, sigcnt, l;
2378   unsigned char lead[4096];
2379
2380   if (!fp)
2381     return rpm_byrpmdbid(0, 0, statep);
2382   if (!state)
2383     {
2384       state = sat_calloc(1, sizeof(*state));
2385       *statep = state;
2386     }
2387   if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
2388     {
2389       fprintf(stderr, "%s: not a rpm\n", name);
2390       return 0;
2391     }
2392   if (lead[78] != 0 || lead[79] != 5)
2393     {
2394       fprintf(stderr, "%s: not a V5 header\n", name);
2395       return 0;
2396     }
2397   if (getu32(lead + 96) != 0x8eade801)
2398     {
2399       fprintf(stderr, "%s: bad signature header\n", name);
2400       return 0;
2401     }
2402   sigcnt = getu32(lead + 96 + 8);
2403   sigdsize = getu32(lead + 96 + 12);
2404   if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
2405     {
2406       fprintf(stderr, "%s: bad signature header\n", name);
2407       return 0;
2408     }
2409   sigdsize += sigcnt * 16;
2410   sigdsize = (sigdsize + 7) & ~7;
2411   headerstart = 96 + 16 + sigdsize;
2412   while (sigdsize)
2413     {
2414       l = sigdsize > 4096 ? 4096 : sigdsize;
2415       if (fread(lead, l, 1, fp) != 1)
2416         {
2417           fprintf(stderr, "%s: unexpected EOF\n", name);
2418           return 0;
2419         }
2420       sigdsize -= l;
2421     }
2422   if (fread(lead, 16, 1, fp) != 1)
2423     {
2424       fprintf(stderr, "%s: unexpected EOF\n", name);
2425       return 0;
2426     }
2427   if (getu32(lead) != 0x8eade801)
2428     {
2429       fprintf(stderr, "%s: bad header\n", name);
2430       fclose(fp);
2431       return 0;
2432     }
2433   sigcnt = getu32(lead + 8);
2434   sigdsize = getu32(lead + 12);
2435   if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
2436     {
2437       fprintf(stderr, "%s: bad header\n", name);
2438       fclose(fp);
2439       return 0;
2440     }
2441   l = sigdsize + sigcnt * 16;
2442   headerend = headerstart + 16 + l;
2443   if (l > state->rpmheadsize)
2444     {
2445       state->rpmheadsize = l + 128;
2446       state->rpmhead = sat_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
2447     }
2448   rpmhead = state->rpmhead;
2449   if (fread(rpmhead->data, l, 1, fp) != 1)
2450     {
2451       fprintf(stderr, "%s: unexpected EOF\n", name);
2452       fclose(fp);
2453       return 0;
2454     }
2455   rpmhead->cnt = sigcnt;
2456   rpmhead->dcnt = sigdsize;
2457   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2458   return rpmhead;
2459 }
2460
2461 void *
2462 rpm_byrpmh(Header h, void **statep)
2463 {
2464   struct rpm_by_state *state = *statep;
2465   const unsigned char *uh;
2466   int sigdsize, sigcnt, l;
2467   RpmHead *rpmhead;
2468
2469   uh = headerUnload(h);
2470   if (!uh)
2471     return 0;
2472   sigcnt = getu32(uh);
2473   sigdsize = getu32(uh + 4);
2474   l = sigdsize + sigcnt * 16;
2475   if (!state)
2476     {
2477       state = sat_calloc(1, sizeof(*state));
2478       *statep = state;
2479     }
2480   if (l > state->rpmheadsize)
2481     {
2482       state->rpmheadsize = l + 128;
2483       state->rpmhead = sat_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
2484     }
2485   rpmhead = state->rpmhead;
2486   memcpy(rpmhead->data, uh + 8, l - 8);
2487   free((void *)uh);
2488   rpmhead->cnt = sigcnt;
2489   rpmhead->dcnt = sigdsize;
2490   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2491   return rpmhead;
2492 }
2493
2494
2495 static char *
2496 r64dec1(char *p, unsigned int *vp, int *eofp)
2497 {
2498   int i, x;
2499   unsigned int v = 0;
2500
2501   for (i = 0; i < 4; )
2502     {
2503       x = *p++;
2504       if (!x)
2505         return 0;
2506       if (x >= 'A' && x <= 'Z')
2507         x -= 'A';
2508       else if (x >= 'a' && x <= 'z')
2509         x -= 'a' - 26;
2510       else if (x >= '0' && x <= '9')
2511         x -= '0' - 52;
2512       else if (x == '+')
2513         x = 62;
2514       else if (x == '/')
2515         x = 63;
2516       else if (x == '=')
2517         {
2518           x = 0;
2519           if (i == 0)
2520             {
2521               *eofp = 3;
2522               *vp = 0;
2523               return p - 1;
2524             }
2525           *eofp += 1;
2526         }
2527       else
2528         continue;
2529       v = v << 6 | x;
2530       i++;
2531     }
2532   *vp = v;
2533   return p;
2534 }
2535
2536 static unsigned int
2537 crc24(unsigned char *p, int len)
2538 {
2539   unsigned int crc = 0xb704ceL;
2540   int i;
2541
2542   while (len--)
2543     {
2544       crc ^= (*p++) << 16;
2545       for (i = 0; i < 8; i++)
2546         if ((crc <<= 1) & 0x1000000)
2547           crc ^= 0x1864cfbL;
2548     }
2549   return crc & 0xffffffL;
2550 }
2551
2552 static unsigned char *
2553 unarmor(char *pubkey, int *pktlp)
2554 {
2555   char *p;
2556   int l, eof;
2557   unsigned char *buf, *bp;
2558   unsigned int v;
2559
2560   *pktlp = 0;
2561   while (strncmp(pubkey, "-----BEGIN PGP PUBLIC KEY BLOCK-----", 36) != 0)
2562     {
2563       pubkey = strchr(pubkey, '\n');
2564       if (!pubkey)
2565         return 0;
2566       pubkey++;
2567     }
2568   pubkey = strchr(pubkey, '\n');
2569   if (!pubkey++)
2570     return 0;
2571   /* skip header lines */
2572   for (;;)
2573     {
2574       while (*pubkey == ' ' || *pubkey == '\t')
2575         pubkey++;
2576       if (*pubkey == '\n')
2577         break;
2578       pubkey = strchr(pubkey, '\n');
2579       if (!pubkey++)
2580         return 0;
2581     }
2582   pubkey++;
2583   p = strchr(pubkey, '=');
2584   if (!p)
2585     return 0;
2586   l = p - pubkey;
2587   bp = buf = sat_malloc(l * 3 / 4 + 4);
2588   eof = 0;
2589   while (!eof)
2590     {
2591       pubkey = r64dec1(pubkey, &v, &eof);
2592       if (!pubkey)
2593         {
2594           sat_free(buf);
2595           return 0;
2596         }
2597       *bp++ = v >> 16;
2598       *bp++ = v >> 8;
2599       *bp++ = v;
2600     }
2601   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
2602     pubkey++;
2603   bp -= eof;
2604   if (*pubkey != '=' || (pubkey = r64dec1(pubkey + 1, &v, &eof)) == 0)
2605     {
2606       sat_free(buf);
2607       return 0;
2608     }
2609   if (v != crc24(buf, bp - buf))
2610     {
2611       sat_free(buf);
2612       return 0;
2613     }
2614   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
2615     pubkey++;
2616   if (strncmp(pubkey, "-----END PGP PUBLIC KEY BLOCK-----", 34) != 0)
2617     {
2618       sat_free(buf);
2619       return 0;
2620     }
2621   *pktlp = bp - buf;
2622   return buf;
2623 }
2624
2625 static void
2626 parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
2627 {
2628   int x, tag, l;
2629   unsigned char keyid[8];
2630   unsigned int kcr = 0, maxex = 0;
2631   unsigned char *pubkey = 0;
2632   int pubkeyl = 0;
2633   unsigned char *userid = 0;
2634   int useridl = 0;
2635
2636   for (; pl; p += l, pl -= l)
2637     {
2638       x = *p++;
2639       pl--;
2640       if (!(x & 128) || pl <= 0)
2641         return;
2642       if ((x & 64) == 0)
2643         {
2644           /* old format */
2645           tag = (x & 0x3c) >> 2;
2646           x &= 3;
2647           if (x == 3)
2648             return;
2649           l = 1 << x;
2650           if (pl < l)
2651             return;
2652           x = 0;
2653           while (l--)
2654             {
2655               x = x << 8 | *p++;
2656               pl--;
2657             }
2658           l = x;
2659         }
2660       else
2661         {
2662           tag = (x & 0x3f);
2663           x = *p++;
2664           pl--;
2665           if (x < 192)
2666             l = x;
2667           else if (x >= 192 && x < 224)
2668             {
2669               if (pl <= 0)
2670                 return;
2671               l = ((x - 192) << 8) + *p++ + 192;
2672               pl--;
2673             }
2674           else if (x == 255)
2675             {
2676               if (pl <= 4)
2677                 return;
2678               l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
2679               p += 4;
2680               pl -= 4;
2681             }
2682           else
2683             return;
2684         }
2685       if (pl < l)
2686         return;
2687       if (tag == 6)
2688         {
2689           pubkey = sat_realloc(pubkey, l);
2690           if (l)
2691             memcpy(pubkey, p, l);
2692           pubkeyl = l;
2693           kcr = 0;
2694           if (p[0] == 3)
2695             {
2696               unsigned int ex;
2697               void *h;
2698               kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
2699               ex = 0;
2700               if (p[5] || p[6])
2701                 {
2702                   ex = kcr + 24*3600 * (p[5] << 8 | p[6]);
2703                   if (ex > maxex)
2704                     maxex = ex;
2705                 }
2706               memset(keyid, 0, 8);
2707               if (p[7] == 1)    /* RSA */
2708                 {
2709                   int i, ql;
2710                   unsigned char fp[16];
2711                   char fpx[32 + 1];
2712                   unsigned char *q;
2713
2714                   ql = ((p[8] << 8 | p[9]) + 7) / 8;
2715                   memcpy(keyid, p + 10 + ql - 8, 8);
2716                   h = sat_chksum_create(REPOKEY_TYPE_MD5);
2717                   sat_chksum_add(h, p + 10, ql);
2718                   q = p + 10 + ql;
2719                   ql = ((q[0] << 8 | q[1]) + 7) / 8;
2720                   sat_chksum_add(h, q + 2, ql);
2721                   sat_chksum_free(h, fp);
2722                   for (i = 0; i < 16; i++)
2723                     sprintf(fpx + i * 2, "%02x", fp[i]);
2724                   setutf8string(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
2725                 }
2726             }
2727           else if (p[0] == 4)
2728             {
2729               int i;
2730               void *h;
2731               unsigned char hdr[3];
2732               unsigned char fp[20];
2733               char fpx[40 + 1];
2734
2735               kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
2736               hdr[0] = 0x99;
2737               hdr[1] = l >> 8;
2738               hdr[2] = l;
2739               h = sat_chksum_create(REPOKEY_TYPE_SHA1);
2740               sat_chksum_add(h, hdr, 3);
2741               sat_chksum_add(h, p, l);
2742               sat_chksum_free(h, fp);
2743               for (i = 0; i < 20; i++)
2744                 sprintf(fpx + i * 2, "%02x", fp[i]);
2745               setutf8string(data, s - s->repo->pool->solvables, PUBKEY_FINGERPRINT, fpx);
2746               memcpy(keyid, fp + 12, 8);
2747             }
2748         }
2749       if (tag == 2)
2750         {
2751           if (p[0] == 3 && p[1] == 5)
2752             {
2753 #if 0
2754               Id htype = 0;
2755 #endif
2756               // printf("V3 signature packet\n");
2757               if (l < 17)
2758                 continue;
2759               if (p[2] != 0x10 && p[2] != 0x11 && p[2] != 0x12 && p[2] != 0x13 && p[2] != 0x1f)
2760                 continue;
2761               if (!memcmp(keyid, p + 6, 8))
2762                 {
2763                   // printf("SELF SIG\n");
2764                 }
2765               else
2766                 {
2767                   // printf("OTHER SIG\n");
2768                 }
2769 #if 0
2770               if (p[16] == 1)
2771                 htype = REPOKEY_TYPE_MD5;
2772               else if (p[16] == 2)
2773                 htype = REPOKEY_TYPE_SHA1;
2774               else if (p[16] == 8)
2775                 htype = REPOKEY_TYPE_SHA256;
2776               if (htype)
2777                 {
2778                   void *h = sat_chksum_create(htype);
2779                   unsigned char b[3], *cs;
2780
2781                   b[0] = 0x99;
2782                   b[1] = pubkeyl >> 8;
2783                   b[2] = pubkeyl;
2784                   sat_chksum_add(h, b, 3);
2785                   sat_chksum_add(h, pubkey, pubkeyl);
2786                   if (p[2] >= 0x10 && p[2] <= 0x13)
2787                     sat_chksum_add(h, userid, useridl);
2788                   sat_chksum_add(h, p + 2, 5);
2789                   cs = sat_chksum_get(h, 0);
2790                   sat_chksum_free(h, 0);
2791                 }
2792 #endif
2793             }
2794           if (p[0] == 4)
2795             {
2796               int j, ql, haveissuer;
2797               unsigned char *q;
2798               unsigned int ex = 0, scr = 0;
2799               unsigned char issuer[8];
2800
2801               // printf("V4 signature packet\n");
2802               if (l < 6)
2803                 continue;
2804               if (p[1] != 0x10 && p[1] != 0x11 && p[1] != 0x12 && p[1] != 0x13 && p[1] != 0x1f)
2805                 continue;
2806               haveissuer = 0;
2807               ex = 0;
2808               q = p + 4;
2809               for (j = 0; q && j < 2; j++)
2810                 {
2811                   if (q + 2 > p + l)
2812                     {
2813                       q = 0;
2814                       break;
2815                     }
2816                   ql = q[0] << 8 | q[1];
2817                   q += 2;
2818                   if (q + ql > p + l)
2819                     {
2820                       q = 0;
2821                       break;
2822                     }
2823                   while (ql)
2824                     {
2825                       int sl;
2826                       x = *q++;
2827                       ql--;
2828                       if (x < 192)
2829                         sl = x;
2830                       else if (x == 255)
2831                         {
2832                           if (ql < 4)
2833                             {
2834                               q = 0;
2835                               break;
2836                             }
2837                           sl = q[0] << 24 | q[1] << 16 | q[2] << 8 | q[3];
2838                           q += 4;
2839                           ql -= 4;
2840                         }
2841                       else
2842                         {
2843                           if (ql < 1)
2844                             {
2845                               q = 0;
2846                               break;
2847                             }
2848                           sl = ((x - 192) << 8) + *q++ + 192;
2849                           ql--;
2850                         }
2851                       if (ql < sl)
2852                         {
2853                           q = 0;
2854                           break;
2855                         }
2856                       x = q[0] & 127;
2857                       // printf("%d SIGSUB %d %d\n", j, x, sl);
2858                       if (x == 16 && sl == 9 && !haveissuer)
2859                         {
2860                           memcpy(issuer, q + 1, 8);
2861                           haveissuer = 1;
2862                         }
2863                       if (x == 2 && j == 0)
2864                         scr = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
2865                       if (x == 9 && j == 0)
2866                         ex = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
2867                       q += sl;
2868                       ql -= sl;
2869                     }
2870                 }
2871               if (ex)
2872                 ex += kcr;
2873               if (haveissuer)
2874                 {
2875 #if 0
2876                   Id htype = 0;
2877                   if (p[3] == 1)
2878                     htype = REPOKEY_TYPE_MD5;
2879                   else if (p[3] == 2)
2880                     htype = REPOKEY_TYPE_SHA1;
2881                   else if (p[3] == 8)
2882                     htype = REPOKEY_TYPE_SHA256;
2883                   if (htype && pubkeyl)
2884                     {
2885                       void *h = sat_chksum_create(htype);
2886                       unsigned char b[6], *cs;
2887                       unsigned int hl;
2888
2889                       b[0] = 0x99;
2890                       b[1] = pubkeyl >> 8;
2891                       b[2] = pubkeyl;
2892                       sat_chksum_add(h, b, 3);
2893                       sat_chksum_add(h, pubkey, pubkeyl);
2894                       if (p[1] >= 0x10 && p[1] <= 0x13)
2895                         {
2896                           b[0] = 0xb4;
2897                           b[1] = useridl >> 24;
2898                           b[2] = useridl >> 16;
2899                           b[3] = useridl >> 8;
2900                           b[4] = useridl;
2901                           sat_chksum_add(h, b, 5);
2902                           sat_chksum_add(h, userid, useridl);
2903                         }
2904                       hl = 6 + (p[4] << 8 | p[5]);
2905                       sat_chksum_add(h, p, hl);
2906                       b[0] = 4;
2907                       b[1] = 0xff;
2908                       b[2] = hl >> 24;
2909                       b[3] = hl >> 16;
2910                       b[4] = hl >> 8;
2911                       b[5] = hl;
2912                       sat_chksum_add(h, b, 6);
2913                       cs = sat_chksum_get(h, 0);
2914                       sat_chksum_free(h, 0);
2915                     }
2916 #endif
2917                   if (!memcmp(keyid, issuer, 8))
2918                     {
2919                       // printf("SELF SIG cr %d ex %d\n", cr, ex);
2920                       if (ex > maxex)
2921                         maxex = ex;
2922                     }
2923                   else
2924                     {
2925                       // printf("OTHER SIG cr %d ex %d\n", cr, ex);
2926                     }
2927                 }
2928             }
2929         }
2930       if (tag == 13)
2931         {
2932           userid = sat_realloc(userid, l);
2933           if (l)
2934             memcpy(userid, p, l);
2935           useridl = l;
2936         }
2937     }
2938   if (maxex)
2939     repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
2940   sat_free(pubkey);
2941   sat_free(userid);
2942 }
2943
2944 /* this is private to rpm, but rpm lacks an interface to retrieve
2945  * the values. Sigh. */
2946 struct pgpDigParams_s {
2947     const char * userid;
2948     const unsigned char * hash;
2949     const char * params[4];
2950     unsigned char tag;
2951     unsigned char version;               /*!< version number. */
2952     unsigned char time[4];               /*!< time that the key was created. */
2953     unsigned char pubkey_algo;           /*!< public key algorithm. */
2954     unsigned char hash_algo;
2955     unsigned char sigtype;
2956     unsigned char hashlen;
2957     unsigned char signhash16[2];
2958     unsigned char signid[8];
2959     unsigned char saved;
2960 };
2961
2962 struct pgpDig_s {
2963     struct pgpDigParams_s signature;
2964     struct pgpDigParams_s pubkey;
2965 };
2966
2967 static int
2968 pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
2969 {
2970   Pool *pool = s->repo->pool;
2971   unsigned char *pkts;
2972   unsigned int btime;
2973   int pktsl, i;
2974   pgpDig dig = 0;
2975   char keyid[16 + 1];
2976   char evrbuf[8 + 1 + 8 + 1];
2977
2978   pkts = unarmor(pubkey, &pktsl);
2979   if (!pkts)
2980     return 0;
2981   setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
2982   parsekeydata(s, data, pkts, pktsl);
2983   /* only rpm knows how to do the release calculation, we don't dare
2984    * to recreate all the bugs */
2985   dig = pgpNewDig();
2986   (void) pgpPrtPkts(pkts, pktsl, dig, 0);
2987   btime = dig->pubkey.time[0] << 24 | dig->pubkey.time[1] << 16 | dig->pubkey.time[2] << 8 | dig->pubkey.signid[3];
2988   sprintf(evrbuf, "%02x%02x%02x%02x-%02x%02x%02x%02x", dig->pubkey.signid[4], dig->pubkey.signid[5], dig->pubkey.signid[6], dig->pubkey.signid[7], dig->pubkey.time[0], dig->pubkey.time[1], dig->pubkey.time[2], dig->pubkey.time[3]);
2989   repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, btime);
2990
2991   s->name = str2id(pool, "gpg-pubkey", 1);
2992   s->evr = str2id(pool, evrbuf, 1);
2993   s->arch = 1;
2994   for (i = 0; i < 8; i++)
2995     sprintf(keyid + 2 * i, "%02x", dig->pubkey.signid[i]);
2996   repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyid);
2997   if (dig->pubkey.userid)
2998     setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, dig->pubkey.userid);
2999   pgpFreeDig(dig);
3000   sat_free((void *)pkts);
3001   return 1;
3002 }
3003
3004 void
3005 repo_add_rpmdb_pubkeys(Repo *repo, const char *rootdir, int flags)
3006 {
3007   Pool *pool = repo->pool;
3008   struct rpm_by_state state;
3009   struct rpmdbentry *entries;
3010   int nentries, i;
3011   char *namedata, *str;
3012   unsigned int u32;
3013   Repodata *data;
3014   Solvable *s;
3015
3016   data = repo_add_repodata(repo, flags);
3017
3018   memset(&state, 0, sizeof(state));
3019   if (!(state.dbenv = opendbenv(rootdir)))
3020     return;
3021   entries = getinstalledrpmdbids(&state, "Name", "gpg-pubkey", &nentries, &namedata);
3022   for (i = 0 ; i < nentries; i++)
3023     {
3024       void *statep = &state;
3025       RpmHead *rpmhead = rpm_byrpmdbid(entries[i].rpmdbid, rootdir, &statep);
3026       if (!rpmhead)
3027         continue;
3028       str = headstring(rpmhead, TAG_DESCRIPTION);
3029       if (!str)
3030         continue;
3031       s = pool_id2solvable(pool, repo_add_solvable(repo));
3032       pubkey2solvable(s, data, str);
3033       u32 = headint32(rpmhead, TAG_INSTALLTIME);
3034       if (u32)
3035         repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, u32);
3036       if (!repo->rpmdbid)
3037         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
3038       repo->rpmdbid[s - pool->solvables - repo->start] = entries[i].rpmdbid;
3039     }
3040   freestate(&state);
3041   if (!(flags & REPO_NO_INTERNALIZE))
3042     repodata_internalize(data);
3043 }
3044
3045 void
3046 repo_add_pubkeys(Repo *repo, const char **keys, int nkeys, int flags)
3047 {
3048   Pool *pool = repo->pool;
3049   Repodata *data;
3050   Solvable *s;
3051   char *buf;
3052   int i, bufl, l, ll;
3053   FILE *fp;
3054
3055   data = repo_add_repodata(repo, flags);
3056   buf = 0;
3057   bufl = 0;
3058   for (i = 0; i < nkeys; i++)
3059     {
3060       if ((fp = fopen(keys[i], "r")) == 0)
3061         {
3062           perror(keys[i]);
3063           continue;
3064         }
3065       for (l = 0; ;)
3066         {
3067           if (bufl - l < 4096)
3068             {
3069               bufl += 4096;
3070               buf = sat_realloc(buf, bufl);
3071             }
3072           ll = fread(buf, 1, bufl - l, fp);
3073           if (ll <= 0)
3074             break;
3075           l += ll;
3076         }
3077       buf[l] = 0;
3078       fclose(fp);
3079       s = pool_id2solvable(pool, repo_add_solvable(repo));
3080       pubkey2solvable(s, data, buf);
3081     }
3082   sat_free(buf);
3083   if (!(flags & REPO_NO_INTERNALIZE))
3084     repodata_internalize(data);
3085 }