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