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