- also parse checksum for debian packages
[platform/upstream/libsolv.git] / examples / solv.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /* solv, a little software installer demoing the sat solver library */
9
10 /* things it does:
11  * - understands globs for package names / dependencies
12  * - understands .arch suffix
13  * - installation of commandline packages
14  * - repository data caching
15  * - on demand loading of secondary repository data
16  * - gpg and checksum verification
17  * - file conflicts
18  * - deltarpm support
19  * - fastestmirror implementation
20  *
21  * things available in the library but missing from solv:
22  * - vendor policy loading
23  * - soft locks file handling
24  * - multi version handling
25  */
26
27 #define _GNU_SOURCE
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <fnmatch.h>
33 #include <unistd.h>
34 #include <zlib.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include <sys/utsname.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <time.h>
41 #include <sys/time.h>
42
43 #include <sys/socket.h>
44 #include <netdb.h>
45 #include <poll.h>
46 #include <errno.h>
47
48 #include "pool.h"
49 #include "poolarch.h"
50 #include "repo.h"
51 #include "evr.h"
52 #include "policy.h"
53 #include "util.h"
54 #include "solver.h"
55 #include "solverdebug.h"
56 #include "chksum.h"
57 #include "repo_solv.h"
58
59 #include "repo_write.h"
60 #ifndef DEBIAN
61 #include "repo_rpmdb.h"
62 #else
63 #include "repo_deb.h"
64 #endif
65 #include "repo_products.h"
66 #include "repo_rpmmd.h"
67 #include "repo_susetags.h"
68 #include "repo_repomdxml.h"
69 #include "repo_updateinfoxml.h"
70 #include "repo_deltainfoxml.h"
71 #include "repo_content.h"
72 #include "pool_fileconflicts.h"
73
74
75 #ifdef FEDORA
76 # define REPOINFO_PATH "/etc/yum.repos.d"
77 #else
78 # define REPOINFO_PATH "/etc/zypp/repos.d"
79 # define PRODUCTS_PATH "/etc/products.d"
80 # define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
81 #endif
82
83 #define SOLVCACHE_PATH "/var/cache/solv"
84
85 #define METADATA_EXPIRE (60 * 90)
86
87 struct repoinfo {
88   Repo *repo;
89
90   char *alias;
91   char *name;
92   int enabled;
93   int autorefresh;
94   char *baseurl;
95   char *metalink;
96   char *mirrorlist;
97   char *path;
98   int type;
99   int pkgs_gpgcheck;
100   int repo_gpgcheck;
101   int priority;
102   int keeppackages;
103   int metadata_expire;
104   char **components;
105   int ncomponents;
106
107   unsigned char cookie[32];
108   unsigned char extcookie[32];
109 };
110
111 #ifdef FEDORA
112 char *
113 yum_substitute(Pool *pool, char *line)
114 {
115   char *p, *p2;
116   static char *releaseevr;
117   static char *basearch;
118
119   if (!line)
120     {
121       sat_free(releaseevr);
122       releaseevr = 0;
123       sat_free(basearch);
124       basearch = 0;
125       return 0;
126     }
127   p = line;
128   while ((p2 = strchr(p, '$')) != 0)
129     {
130       if (!strncmp(p2, "$releasever", 11))
131         {
132           if (!releaseevr)
133             {
134               Queue q;
135         
136               queue_init(&q);
137               rpm_installedrpmdbids(0, "Providename", "redhat-release", &q);
138               if (q.count)
139                 {
140                   void *handle, *state = 0;
141                   char *p;
142                   handle = rpm_byrpmdbid(q.elements[0], 0, &state);
143                   releaseevr = rpm_query(handle, SOLVABLE_EVR);
144                   rpm_byrpmdbid(0, 0, &state);
145                   if ((p = strchr(releaseevr, '-')) != 0)
146                     *p = 0;
147                 }
148               queue_free(&q);
149               if (!releaseevr)
150                 releaseevr = strdup("?");
151             }
152           *p2 = 0;
153           p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
154           p2 = p + (p2 - line);
155           line = p;
156           p = p2 + strlen(releaseevr);
157           continue;
158         }
159       if (!strncmp(p2, "$basearch", 9))
160         {
161           if (!basearch)
162             {
163               struct utsname un;
164               if (uname(&un))
165                 {
166                   perror("uname");
167                   exit(1);
168                 }
169               basearch = strdup(un.machine);
170               if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
171                 basearch[1] = '3';
172             }
173           *p2 = 0;
174           p = pool_tmpjoin(pool, line, basearch, p2 + 9);
175           p2 = p + (p2 - line);
176           line = p;
177           p = p2 + strlen(basearch);
178           continue;
179         }
180       p = p2 + 1;
181     }
182   return line;
183 }
184 #endif
185
186 #define TYPE_UNKNOWN    0
187 #define TYPE_SUSETAGS   1
188 #define TYPE_RPMMD      2
189 #define TYPE_PLAINDIR   3
190 #define TYPE_DEBIAN     4
191
192 static int
193 read_repoinfos_sort(const void *ap, const void *bp)
194 {
195   const struct repoinfo *a = ap;
196   const struct repoinfo *b = bp;
197   return strcmp(a->alias, b->alias);
198 }
199
200 #ifndef DEBIAN
201
202 struct repoinfo *
203 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
204 {
205   char buf[4096];
206   char buf2[4096], *kp, *vp, *kpe;
207   DIR *dir;
208   FILE *fp;
209   struct dirent *ent;
210   int l, rdlen;
211   struct repoinfo *repoinfos = 0, *cinfo;
212   int nrepoinfos = 0;
213
214   rdlen = strlen(reposdir);
215   dir = opendir(reposdir);
216   if (!dir)
217     {
218       *nrepoinfosp = 0;
219       return 0;
220     }
221   while ((ent = readdir(dir)) != 0)
222     {
223       l = strlen(ent->d_name);
224       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
225         continue;
226       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
227       if ((fp = fopen(buf, "r")) == 0)
228         {
229           perror(buf);
230           continue;
231         }
232       cinfo = 0;
233       while(fgets(buf2, sizeof(buf2), fp))
234         {
235           l = strlen(buf2);
236           if (l == 0)
237             continue;
238           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
239             buf2[--l] = 0;
240           kp = buf2;
241           while (*kp == ' ' || *kp == '\t')
242             kp++;
243           if (!*kp || *kp == '#')
244             continue;
245 #ifdef FEDORA
246           if (strchr(kp, '$'))
247             kp = yum_substitute(pool, kp);
248 #endif
249           if (*kp == '[')
250             {
251               vp = strrchr(kp, ']');
252               if (!vp)
253                 continue;
254               *vp = 0;
255               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
256               cinfo = repoinfos + nrepoinfos++;
257               memset(cinfo, 0, sizeof(*cinfo));
258               cinfo->alias = strdup(kp + 1);
259               cinfo->type = TYPE_RPMMD;
260               cinfo->autorefresh = 1;
261               cinfo->priority = 99;
262 #ifndef FEDORA
263               cinfo->repo_gpgcheck = 1;
264 #endif
265               cinfo->metadata_expire = METADATA_EXPIRE;
266               continue;
267             }
268           if (!cinfo)
269             continue;
270           vp = strchr(kp, '=');
271           if (!vp)
272             continue;
273           for (kpe = vp - 1; kpe >= kp; kpe--)
274             if (*kpe != ' ' && *kpe != '\t')
275               break;
276           if (kpe == kp)
277             continue;
278           vp++;
279           while (*vp == ' ' || *vp == '\t')
280             vp++;
281           kpe[1] = 0;
282           if (!strcmp(kp, "name"))
283             cinfo->name = strdup(vp);
284           else if (!strcmp(kp, "enabled"))
285             cinfo->enabled = *vp == '0' ? 0 : 1;
286           else if (!strcmp(kp, "autorefresh"))
287             cinfo->autorefresh = *vp == '0' ? 0 : 1;
288           else if (!strcmp(kp, "gpgcheck"))
289             cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
290           else if (!strcmp(kp, "repo_gpgcheck"))
291             cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
292           else if (!strcmp(kp, "baseurl"))
293             cinfo->baseurl = strdup(vp);
294           else if (!strcmp(kp, "mirrorlist"))
295             {
296               if (strstr(vp, "metalink"))
297                 cinfo->metalink = strdup(vp);
298               else
299                 cinfo->mirrorlist = strdup(vp);
300             }
301           else if (!strcmp(kp, "path"))
302             {
303               if (vp && strcmp(vp, "/") != 0)
304                 cinfo->path = strdup(vp);
305             }
306           else if (!strcmp(kp, "type"))
307             {
308               if (!strcmp(vp, "yast2"))
309                 cinfo->type = TYPE_SUSETAGS;
310               else if (!strcmp(vp, "rpm-md"))
311                 cinfo->type = TYPE_RPMMD;
312               else if (!strcmp(vp, "plaindir"))
313                 cinfo->type = TYPE_PLAINDIR;
314               else
315                 cinfo->type = TYPE_UNKNOWN;
316             }
317           else if (!strcmp(kp, "priority"))
318             cinfo->priority = atoi(vp);
319           else if (!strcmp(kp, "keeppackages"))
320             cinfo->keeppackages = *vp == '0' ? 0 : 1;
321         }
322       fclose(fp);
323       cinfo = 0;
324     }
325   closedir(dir);
326   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
327   *nrepoinfosp = nrepoinfos;
328   return repoinfos;
329 }
330
331 #else
332
333 struct repoinfo *
334 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
335 {
336   FILE *fp;
337   char buf[4096];
338   char buf2[4096];
339   int l;
340   char *kp, *url, *distro;
341   struct repoinfo *repoinfos = 0, *cinfo;
342   int nrepoinfos = 0;
343   DIR *dir = 0;
344   struct dirent *ent;
345
346   fp = fopen("/etc/apt/sources.list", "r");
347   while (1)
348     {
349       if (!fp)
350         {
351           if (!dir)
352             {
353               dir = opendir("/etc/apt/sources.list.d");
354               if (!dir)
355                 break;
356             }
357           if ((ent = readdir(dir)) == 0)
358             {
359               closedir(dir);
360               break;
361             }
362           l = strlen(ent->d_name);
363           if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
364             continue;
365           snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
366           if (!(fp = fopen(buf, "r")))
367             continue;
368         }
369       while(fgets(buf2, sizeof(buf2), fp))
370         {
371           l = strlen(buf2);
372           if (l == 0)
373             continue;
374           while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
375             buf2[--l] = 0;
376           kp = buf2;
377           while (*kp == ' ' || *kp == '\t')
378             kp++;
379           if (!*kp || *kp == '#')
380             continue;
381           if (strncmp(kp, "deb", 3) != 0)
382             continue;
383           kp += 3;
384           if (*kp != ' ' && *kp != '\t')
385             continue;
386           while (*kp == ' ' || *kp == '\t')
387             kp++;
388           if (!*kp)
389             continue;
390           url = kp;
391           while (*kp && *kp != ' ' && *kp != '\t')
392             kp++;
393           if (*kp)
394             *kp++ = 0;
395           while (*kp == ' ' || *kp == '\t')
396             kp++;
397           if (!*kp)
398             continue;
399           distro = kp;
400           while (*kp && *kp != ' ' && *kp != '\t')
401             kp++;
402           if (*kp)
403             *kp++ = 0;
404           while (*kp == ' ' || *kp == '\t')
405             kp++;
406           if (!*kp)
407             continue;
408           repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
409           cinfo = repoinfos + nrepoinfos++;
410           cinfo->baseurl = strdup(url);
411           cinfo->alias = sat_dupjoin(url, "/", distro);
412           cinfo->name = strdup(distro);
413           cinfo->type = TYPE_DEBIAN;
414           cinfo->enabled = 1;
415           cinfo->autorefresh = 1;
416           cinfo->repo_gpgcheck = 1;
417           cinfo->metadata_expire = METADATA_EXPIRE;
418           while (*kp)
419             {
420               char *compo;
421               while (*kp == ' ' || *kp == '\t')
422                 kp++;
423               if (!*kp)
424                 break;
425               compo = kp;
426               while (*kp && *kp != ' ' && *kp != '\t')
427                 kp++;
428               if (*kp)
429                 *kp++ = 0;
430               cinfo->components = sat_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
431               cinfo->components[cinfo->ncomponents++] = strdup(compo);
432             }
433         }
434       fclose(fp);
435       fp = 0;
436     }
437   qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
438   *nrepoinfosp = nrepoinfos;
439   return repoinfos;
440 }
441
442 #endif
443
444 void
445 free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
446 {
447   int i, j;
448   for (i = 0; i < nrepoinfos; i++)
449     {
450       struct repoinfo *cinfo = repoinfos + i;
451       sat_free(cinfo->name);
452       sat_free(cinfo->alias);
453       sat_free(cinfo->path);
454       sat_free(cinfo->metalink);
455       sat_free(cinfo->mirrorlist);
456       sat_free(cinfo->baseurl);
457       for (j = 0; j < cinfo->ncomponents; j++)
458         sat_free(cinfo->components[j]);
459       sat_free(cinfo->components);
460     }
461   sat_free(repoinfos);
462 }
463
464 static inline int
465 opentmpfile()
466 {
467   char tmpl[100];
468   int fd;
469
470   strcpy(tmpl, "/var/tmp/solvXXXXXX");
471   fd = mkstemp(tmpl);
472   if (fd < 0)
473     {
474       perror("mkstemp");
475       exit(1);
476     }
477   unlink(tmpl);
478   return fd;
479 }
480
481 static int
482 verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
483 {
484   char buf[1024];
485   unsigned char *sum;
486   void *h;
487   int l;
488
489   h = sat_chksum_create(chksumtype);
490   if (!h)
491     {
492       printf("%s: unknown checksum type\n", file);
493       return 0;
494     }
495   while ((l = read(fd, buf, sizeof(buf))) > 0)
496     sat_chksum_add(h, buf, l);
497   lseek(fd, 0, SEEK_SET);
498   l = 0;
499   sum = sat_chksum_get(h, &l);
500   if (memcmp(sum, chksum, l))
501     {
502       printf("%s: checksum mismatch\n", file);
503       sat_chksum_free(h, 0);
504       return 0;
505     }
506   sat_chksum_free(h, 0);
507   return 1;
508 }
509
510 void
511 findfastest(char **urls, int nurls)
512 {
513   int i, j, port;
514   int *socks, qc;
515   struct pollfd *fds;
516   char *p, *p2, *q;
517   char portstr[16];
518   struct addrinfo hints, *result;;
519
520   fds = sat_calloc(nurls, sizeof(*fds));
521   socks = sat_calloc(nurls, sizeof(*socks));
522   for (i = 0; i < nurls; i++)
523     {
524       socks[i] = -1;
525       p = strchr(urls[i], '/');
526       if (!p)
527         continue;
528       if (p[1] != '/')
529         continue;
530       p += 2;
531       q = strchr(p, '/');
532       qc = 0;
533       if (q)
534         {
535           qc = *q;
536           *q = 0;
537         }
538       if ((p2 = strchr(p, '@')) != 0)
539         p = p2 + 1;
540       port = 80;
541       if (!strncmp("https:", urls[i], 6))
542         port = 443;
543       else if (!strncmp("ftp:", urls[i], 4))
544         port = 21;
545       if ((p2 = strrchr(p, ':')) != 0)
546         {
547           port = atoi(p2 + 1);
548           if (q)
549             *q = qc;
550           q = p2;
551           qc = *q;
552           *q = 0;
553         }
554       sprintf(portstr, "%d", port);
555       memset(&hints, 0, sizeof(struct addrinfo));
556       hints.ai_family = AF_UNSPEC;
557       hints.ai_socktype = SOCK_STREAM;
558       hints.ai_flags = AI_NUMERICSERV;
559       result = 0;
560       if (!getaddrinfo(p, portstr, &hints, &result))
561         {
562           socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
563           if (socks[i] >= 0)
564             {
565               fcntl(socks[i], F_SETFL, O_NONBLOCK);
566               if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
567                 {
568                   if (errno != EINPROGRESS)
569                     {
570                       close(socks[i]);
571                       socks[i] = -1;
572                     }
573                 }
574             }
575           freeaddrinfo(result);
576         }
577       if (q)
578         *q = qc;
579     }
580   for (;;)
581     {
582       for (i = j = 0; i < nurls; i++)
583         {
584           if (socks[i] < 0)
585             continue;
586           fds[j].fd = socks[i];
587           fds[j].events = POLLOUT;
588           j++;
589         }
590       if (j < 2)
591         {
592           i = j - 1;
593           break;
594         }
595       if (poll(fds, j, 10000) <= 0)
596         {
597           i = -1;       /* something is wrong */
598           break;
599         }
600       for (i = 0; i < j; i++)
601         if ((fds[i].revents & POLLOUT) != 0)
602           {
603             int soe = 0;
604             socklen_t soel = sizeof(int);
605             if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
606               {
607                 /* connect failed, kill socket */
608                 for (j = 0; j < nurls; j++)
609                   if (socks[j] == fds[i].fd)
610                     {
611                       close(socks[j]);
612                       socks[j] = -1;
613                     }
614                 i = j + 1;
615                 break;
616               }
617             break;      /* horray! */
618           }
619       if (i == j + 1)
620         continue;
621       if (i == j)
622         i = -1;         /* something is wrong, no bit was set */
623       break;
624     }
625   /* now i contains the fastest fd index */
626   if (i >= 0)
627     {
628       for (j = 0; j < nurls; j++)
629         if (socks[j] == fds[i].fd)
630           break;
631       if (j != 0)
632         {
633           char *url0 = urls[0];
634           urls[0] = urls[j];
635           urls[j] = url0;
636         }
637     }
638   for (i = j = 0; i < nurls; i++)
639     if (socks[i] >= 0)
640       close(socks[i]);
641   free(socks);
642   free(fds);
643 }
644
645 char *
646 findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
647 {
648   char buf[4096], *bp, *ep;
649   char **urls = 0;
650   int nurls = 0;
651   int i;
652
653   if (chksumtypep)
654     *chksumtypep = 0;
655   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
656     {
657       while (*bp == ' ' || *bp == '\t')
658         bp++;
659       if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
660         {
661           int i;
662
663           bp += 20;
664           memset(chksump, 0, 32);
665           for (i = 0; i < 64; i++)
666             {
667               int c = *bp++;
668               if (c >= '0' && c <= '9')
669                 chksump[i / 2] = chksump[i / 2] * 16 + (c - '0');
670               else if (c >= 'a' && c <= 'f')
671                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('a' - 10));
672               else if (c >= 'A' && c <= 'F')
673                 chksump[i / 2] = chksump[i / 2] * 16 + (c - ('A' - 10));
674               else
675                 break;
676             }
677           if (i == 64)
678             *chksumtypep = REPOKEY_TYPE_SHA256;
679           continue;
680         }
681       if (strncmp(bp, "<url", 4))
682         continue;
683       bp = strchr(bp, '>');
684       if (!bp)
685         continue;
686       bp++;
687       ep = strstr(bp, "repodata/repomd.xml</url>");
688       if (!ep)
689         continue;
690       *ep = 0;
691       if (strncmp(bp, "http", 4))
692         continue;
693       urls = sat_extend(urls, nurls, 1, sizeof(*urls), 15);
694       urls[nurls++] = strdup(bp);
695     }
696   if (nurls)
697     {
698       if (nurls > 1)
699         findfastest(urls, nurls > 5 ? 5 : nurls);
700       bp = urls[0];
701       urls[0] = 0;
702       for (i = 0; i < nurls; i++)
703         sat_free(urls[i]);
704       sat_free(urls);
705       ep = strchr(bp, '/');
706       if ((ep = strchr(ep + 2, '/')) != 0)
707         {
708           *ep = 0;
709           printf("[using mirror %s]\n", bp);
710           *ep = '/';
711         }
712       return bp;
713     }
714   return 0;
715 }
716
717 char *
718 findmirrorlisturl(FILE *fp)
719 {
720   char buf[4096], *bp, *ep;
721   int i, l;
722   char **urls = 0;
723   int nurls = 0;
724
725   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
726     {
727       while (*bp == ' ' || *bp == '\t')
728         bp++;
729       if (!*bp || *bp == '#')
730         continue;
731       l = strlen(bp);
732       while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
733         bp[--l] = 0;
734       urls = sat_extend(urls, nurls, 1, sizeof(*urls), 15);
735       urls[nurls++] = strdup(bp);
736     }
737   if (nurls)
738     {
739       if (nurls > 1)
740         findfastest(urls, nurls > 5 ? 5 : nurls);
741       bp = urls[0];
742       urls[0] = 0;
743       for (i = 0; i < nurls; i++)
744         sat_free(urls[i]);
745       sat_free(urls);
746       ep = strchr(bp, '/');
747       if ((ep = strchr(ep + 2, '/')) != 0)
748         {
749           *ep = 0;
750           printf("[using mirror %s]\n", bp);
751           *ep = '/';
752         }
753       return bp;
754     }
755   return 0;
756 }
757
758 static ssize_t
759 cookie_gzread(void *cookie, char *buf, size_t nbytes)
760 {
761   return gzread((gzFile *)cookie, buf, nbytes);
762 }
763
764 static int
765 cookie_gzclose(void *cookie)
766 {
767   return gzclose((gzFile *)cookie);
768 }
769
770 FILE *
771 curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int *badchecksump)
772 {
773   pid_t pid;
774   int fd, l;
775   int status;
776   char url[4096];
777   const char *baseurl = cinfo->baseurl;
778
779   if (!baseurl)
780     {
781       if (!cinfo->metalink && !cinfo->mirrorlist)
782         return 0;
783       if (file != cinfo->metalink && file != cinfo->mirrorlist)
784         {
785           FILE *fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
786           unsigned char mlchksum[32];
787           Id mlchksumtype = 0;
788           if (!fp)
789             return 0;
790           if (cinfo->metalink)
791             cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
792           else
793             cinfo->baseurl = findmirrorlisturl(fp);
794           fclose(fp);
795           if (!cinfo->baseurl)
796             return 0;
797 #ifdef FEDORA
798           if (strchr(cinfo->baseurl, '$'))
799             {
800               char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
801               free(cinfo->baseurl);
802               cinfo->baseurl = strdup(b);
803             }
804 #endif
805           if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
806             {
807               chksumtype = mlchksumtype;
808               chksum = mlchksum;
809             }
810           return curlfopen(cinfo, file, uncompress, chksum, chksumtype, badchecksump);
811         }
812       snprintf(url, sizeof(url), "%s", file);
813     }
814   else
815     {
816       l = strlen(baseurl);
817       if (l && baseurl[l - 1] == '/')
818         snprintf(url, sizeof(url), "%s%s", baseurl, file);
819       else
820         snprintf(url, sizeof(url), "%s/%s", baseurl, file);
821     }
822   fd = opentmpfile();
823   // printf("url: %s\n", url);
824   if ((pid = fork()) == (pid_t)-1)
825     {
826       perror("fork");
827       exit(1);
828     }
829   if (pid == 0)
830     {
831       if (fd != 1)
832         {
833           dup2(fd, 1);
834           close(fd);
835         }
836       execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
837       perror("curl");
838       _exit(0);
839     }
840   status = 0;
841   while (waitpid(pid, &status, 0) != pid)
842     ;
843   if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
844     {
845       /* empty file */
846       close(fd);
847       return 0;
848     }
849   lseek(fd, 0, SEEK_SET);
850   if (status)
851     {
852       printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
853       if (badchecksump)
854         *badchecksump = 1;
855       close(fd);
856       return 0;
857     }
858   if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
859     {
860       if (badchecksump)
861         *badchecksump = 1;
862       close(fd);
863       return 0;
864     }
865   if (uncompress)
866     {
867       char tmpl[100];
868       cookie_io_functions_t cio;
869       gzFile *gzf;
870
871       sprintf(tmpl, "/dev/fd/%d", fd);
872       gzf = gzopen(tmpl, "r");
873       close(fd);
874       if (!gzf)
875         {
876           fprintf(stderr, "could not open /dev/fd/%d, /proc not mounted?\n", fd);
877           exit(1);
878         }
879       memset(&cio, 0, sizeof(cio));
880       cio.read = cookie_gzread;
881       cio.close = cookie_gzclose;
882       return fopencookie(gzf, "r", cio);
883     }
884   fcntl(fd, F_SETFD, FD_CLOEXEC);
885   return fdopen(fd, "r");
886 }
887
888 #ifndef DEBIAN
889
890 static void
891 cleanupgpg(char *gpgdir)
892 {
893   char cmd[256];
894   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
895   unlink(cmd);
896   snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
897   unlink(cmd);
898   snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
899   unlink(cmd);
900   snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
901   unlink(cmd);
902   snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
903   unlink(cmd);
904   rmdir(gpgdir);
905 }
906
907 int
908 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
909 {
910   char *gpgdir;
911   char *keysfile;
912   const char *pubkey;
913   char cmd[256];
914   FILE *kfp;
915   Solvable *s;
916   Id p;
917   off_t posfp, possigfp;
918   int r, nkeys;
919
920   gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
921   if (!gpgdir)
922     return 0;
923   keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
924   if (!(kfp = fopen(keysfile, "w")) )
925     {
926       cleanupgpg(gpgdir);
927       return 0;
928     }
929   nkeys = 0;
930   for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
931     {
932       if (!s->repo)
933         continue;
934       pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
935       if (!pubkey || !*pubkey)
936         continue;
937       if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
938         break;
939       if (fputc('\n', kfp) == EOF)      /* Just in case... */
940         break;
941       nkeys++;
942     }
943   if (fclose(kfp) || !nkeys)
944     {
945       cleanupgpg(gpgdir);
946       return 0;
947     }
948   snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
949   if (system(cmd))
950     {
951       fprintf(stderr, "key import error\n");
952       cleanupgpg(gpgdir);
953       return 0;
954     }
955   unlink(keysfile);
956   posfp = lseek(fileno(fp), 0, SEEK_CUR);
957   lseek(fileno(fp), 0, SEEK_SET);
958   possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
959   lseek(fileno(sigfp), 0, SEEK_SET);
960   snprintf(cmd, sizeof(cmd), "gpg -q --homedir %s --verify /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, fileno(sigfp), fileno(fp));
961   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
962   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
963   r = system(cmd);
964   lseek(fileno(sigfp), possigfp, SEEK_SET);
965   lseek(fileno(fp), posfp, SEEK_SET);
966   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
967   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
968   cleanupgpg(gpgdir);
969   return r == 0 ? 1 : 0;
970 }
971
972 #else
973
974 int
975 checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
976 {
977   char cmd[256];
978   int r;
979
980   snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
981   fcntl(fileno(fp), F_SETFD, 0);        /* clear CLOEXEC */
982   fcntl(fileno(sigfp), F_SETFD, 0);     /* clear CLOEXEC */
983   r = system(cmd);
984   fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
985   fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
986   return r == 0 ? 1 : 0;
987 }
988
989 #endif
990
991 #define CHKSUM_IDENT "1.1"
992
993 void
994 calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
995 {
996   char buf[4096];
997   void *h = sat_chksum_create(chktype);
998   int l;
999
1000   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
1001   while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
1002     sat_chksum_add(h, buf, l);
1003   rewind(fp);
1004   sat_chksum_free(h, out);
1005 }
1006
1007 void
1008 calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *out)
1009 {
1010   void *h = sat_chksum_create(chktype);
1011   sat_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
1012   sat_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
1013   sat_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
1014   sat_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
1015   sat_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
1016   sat_chksum_free(h, out);
1017 }
1018
1019 void
1020 setarch(Pool *pool)
1021 {
1022   struct utsname un;
1023   if (uname(&un))
1024     {
1025       perror("uname");
1026       exit(1);
1027     }
1028   pool_setarch(pool, un.machine);
1029 }
1030
1031 char *calccachepath(Repo *repo, const char *repoext)
1032 {
1033   char *q, *p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", repo->name);
1034   if (repoext)
1035     {
1036       p = pool_tmpjoin(repo->pool, p, "_", repoext);
1037       p = pool_tmpjoin(repo->pool, p, ".solvx", 0);
1038     }
1039   else
1040     p = pool_tmpjoin(repo->pool, p, ".solv", 0);
1041   q = p + strlen(SOLVCACHE_PATH) + 1;
1042   if (*q == '.')
1043     *q = '_';
1044   for (; *q; q++)
1045     if (*q == '/')
1046       *q = '_';
1047   return p;
1048 }
1049
1050 int
1051 usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
1052 {
1053   FILE *fp;
1054   unsigned char mycookie[32];
1055   unsigned char myextcookie[32];
1056   struct repoinfo *cinfo;
1057   int flags;
1058
1059   cinfo = repo->appdata;
1060   if (!(fp = fopen(calccachepath(repo, repoext), "r")))
1061     return 0;
1062   if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
1063     {
1064       fclose(fp);
1065       return 0;
1066     }
1067   if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
1068     {
1069       fclose(fp);
1070       return 0;
1071     }
1072   if (cinfo && !repoext)
1073     {
1074       if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
1075         {
1076           fclose(fp);
1077           return 0;
1078         }
1079     }
1080   rewind(fp);
1081
1082   flags = 0;
1083   if (repoext)
1084     {
1085       flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
1086       if (strcmp(repoext, "DL") != 0)
1087         flags |= REPO_LOCALPOOL;        /* no local pool for DL so that we can compare IDs */
1088     }
1089
1090   if (repo_add_solv_flags(repo, fp, flags))
1091     {
1092       fclose(fp);
1093       return 0;
1094     }
1095   if (cinfo && !repoext)
1096     {
1097       memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
1098       memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
1099     }
1100   if (mark)
1101     futimes(fileno(fp), 0);     /* try to set modification time */
1102   fclose(fp);
1103   return 1;
1104 }
1105
1106 void
1107 writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
1108 {
1109   FILE *fp;
1110   int i, fd;
1111   char *tmpl;
1112   struct repoinfo *cinfo;
1113   int onepiece;
1114
1115   cinfo = repo->appdata;
1116   mkdir(SOLVCACHE_PATH, 0755);
1117   tmpl = sat_dupjoin(SOLVCACHE_PATH, "/", ".newsolv-XXXXXX");
1118   fd = mkstemp(tmpl);
1119   if (fd < 0)
1120     {
1121       free(tmpl);
1122       return;
1123     }
1124   fchmod(fd, 0444);
1125   if (!(fp = fdopen(fd, "w")))
1126     {
1127       close(fd);
1128       unlink(tmpl);
1129       free(tmpl);
1130       return;
1131     }
1132
1133   onepiece = 1;
1134   for (i = repo->start; i < repo->end; i++)
1135    if (repo->pool->solvables[i].repo != repo)
1136      break;
1137   if (i < repo->end)
1138     onepiece = 0;
1139
1140   if (!info)
1141     repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
1142   else if (repoext)
1143     repodata_write(info, fp, repo_write_stdkeyfilter, 0);
1144   else
1145     {
1146       int oldnrepodata = repo->nrepodata;
1147       repo->nrepodata = 1;      /* XXX: do this right */
1148       repo_write(repo, fp, repo_write_stdkeyfilter, 0, 0);
1149       repo->nrepodata = oldnrepodata;
1150       onepiece = 0;
1151     }
1152
1153   if (!repoext && cinfo)
1154     {
1155       if (!cinfo->extcookie[0])
1156         {
1157           /* create the ext cookie and append it */
1158           /* we just need some unique ID */
1159           struct stat stb;
1160           if (!fstat(fileno(fp), &stb))
1161             {
1162               int i;
1163
1164               calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->extcookie);
1165               for (i = 0; i < 32; i++)
1166                 cinfo->extcookie[i] ^= cookie[i];
1167             }
1168           if (cinfo->extcookie[0] == 0)
1169             cinfo->extcookie[0] = 1;
1170         }
1171       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
1172         {
1173           fclose(fp);
1174           unlink(tmpl);
1175           free(tmpl);
1176           return;
1177         }
1178     }
1179   /* append our cookie describing the metadata state */
1180   if (fwrite(cookie, 32, 1, fp) != 1)
1181     {
1182       fclose(fp);
1183       unlink(tmpl);
1184       free(tmpl);
1185       return;
1186     }
1187   if (fclose(fp))
1188     {
1189       unlink(tmpl);
1190       free(tmpl);
1191       return;
1192     }
1193   if (onepiece)
1194     {
1195       /* switch to just saved repo to activate paging and save memory */
1196       FILE *fp = fopen(tmpl, "r");
1197       if (fp)
1198         {
1199           if (!repoext)
1200             {
1201               /* main repo */
1202               repo_empty(repo, 1);
1203               if (repo_add_solv_flags(repo, fp, SOLV_ADD_NO_STUBS))
1204                 {
1205                   /* oops, no way to recover from here */
1206                   fprintf(stderr, "internal error\n");
1207                   exit(1);
1208                 }
1209             }
1210           else
1211             {
1212               /* make sure repodata contains complete repo */
1213               /* (this is how repodata_write saves it) */
1214               repodata_extend_block(info, repo->start, repo->end - repo->start);
1215               info->state = REPODATA_LOADING;
1216               /* no need for LOCALPOOL as pool already contains ids */
1217               repo_add_solv_flags(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1218               info->state = REPODATA_AVAILABLE; /* in case the load failed */
1219             }
1220           fclose(fp);
1221         }
1222     }
1223   if (!rename(tmpl, calccachepath(repo, repoext)))
1224     unlink(tmpl);
1225   free(tmpl);
1226 }
1227
1228
1229 static Pool *
1230 read_sigs()
1231 {
1232   Pool *sigpool = pool_create();
1233 #ifndef DEBIAN
1234   Repo *repo = repo_create(sigpool, "rpmdbkeys");
1235   repo_add_rpmdb_pubkeys(repo, 0, 0);
1236 #endif
1237   return sigpool;
1238 }
1239
1240
1241 /* repomd helpers */
1242
1243 static inline const char *
1244 repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1245 {
1246   Pool *pool = repo->pool;
1247   Dataiterator di;
1248   const char *filename;
1249
1250   filename = 0;
1251   *chksump = 0;
1252   *chksumtypep = 0;
1253   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1254   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1255   if (dataiterator_step(&di))
1256     {
1257       dataiterator_setpos_parent(&di);
1258       filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1259       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
1260     }
1261   dataiterator_free(&di);
1262   if (filename && !*chksumtypep)
1263     {
1264       printf("no %s file checksum!\n", what);
1265       filename = 0;
1266     }
1267   return filename;
1268 }
1269
1270 int
1271 repomd_add_ext(Repo *repo, Repodata *data, const char *what)
1272 {
1273   Pool *pool = repo->pool;
1274   Dataiterator di;
1275   Id chksumtype, handle;
1276   const unsigned char *chksum;
1277   const char *filename;
1278
1279   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1280   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1281   if (!dataiterator_step(&di))
1282     {
1283       dataiterator_free(&di);
1284       return 0;
1285     }
1286   if (!strcmp(what, "prestodelta"))
1287     what = "deltainfo";
1288   dataiterator_setpos_parent(&di);
1289   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1290   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
1291   if (!filename || !chksum)
1292     {
1293       dataiterator_free(&di);
1294       return 0;
1295     }
1296   handle = repodata_new_handle(data);
1297   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1298   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1299   if (chksumtype)
1300     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1301   if (!strcmp(what, "deltainfo"))
1302     {
1303       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1304       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1305     }
1306   if (!strcmp(what, "filelists"))
1307     {
1308       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1309       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1310     }
1311   dataiterator_free(&di);
1312   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1313   return 1;
1314 }
1315
1316
1317 /* susetags helpers */
1318
1319 static inline const char *
1320 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1321 {
1322   Pool *pool = repo->pool;
1323   Dataiterator di;
1324   const char *filename;
1325
1326   filename = 0;
1327   *chksump = 0;
1328   *chksumtypep = 0;
1329   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1330   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1331   if (dataiterator_step(&di))
1332     {
1333       dataiterator_setpos_parent(&di);
1334       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1335       filename = what;
1336     }
1337   dataiterator_free(&di);
1338   if (filename && !*chksumtypep)
1339     {
1340       printf("no %s file checksum!\n", what);
1341       filename = 0;
1342     }
1343   return filename;
1344 }
1345
1346 static Id susetags_langtags[] = {
1347   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1348   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1349   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1350   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1351   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1352   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1353   0, 0
1354 };
1355
1356 void
1357 susetags_add_ext(Repo *repo, Repodata *data)
1358 {
1359   Pool *pool = repo->pool;
1360   Dataiterator di;
1361   char ext[3];
1362   Id handle, filechksumtype;
1363   const unsigned char *filechksum;
1364   int i;
1365
1366   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1367   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1368   while (dataiterator_step(&di))
1369     {
1370       if (strncmp(di.kv.str, "packages.", 9) != 0)
1371         continue;
1372       if (!strcmp(di.kv.str + 9, "gz"))
1373         continue;
1374       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1375         continue;
1376       ext[0] = di.kv.str[9];
1377       ext[1] = di.kv.str[10];
1378       ext[2] = 0;
1379       if (!strcmp(ext, "en"))
1380         continue;
1381       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1382         continue;
1383       handle = repodata_new_handle(data);
1384       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1385       if (filechksumtype)
1386         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1387       if (!strcmp(ext, "DU"))
1388         {
1389           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1390           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1391         }
1392       else if (!strcmp(ext, "FL"))
1393         {
1394           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1395           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1396         }
1397       else
1398         {
1399           for (i = 0; susetags_langtags[i]; i += 2)
1400             {
1401               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1402               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1403             }
1404         }
1405       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1406     }
1407   dataiterator_free(&di);
1408 }
1409
1410
1411 static inline int
1412 iscompressed(const char *name)
1413 {
1414   int l = strlen(name);
1415   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1416 }
1417
1418
1419 /* load callback */
1420
1421 int
1422 load_stub(Pool *pool, Repodata *data, void *dp)
1423 {
1424   const char *filename, *descrdir, *repomdtype;
1425   const unsigned char *filechksum;
1426   Id filechksumtype;
1427   struct repoinfo *cinfo;
1428   FILE *fp;
1429   Id defvendor;
1430   char ext[3];
1431
1432   cinfo = data->repo->appdata;
1433
1434   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1435   if (filename)
1436     {
1437       /* susetags load */
1438       ext[0] = filename[9];
1439       ext[1] = filename[10];
1440       ext[2] = 0;
1441 #if 1
1442       printf("[%s:%s", data->repo->name, ext);
1443 #endif
1444       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1445         {
1446           printf(" cached]\n"); fflush(stdout);
1447           return 1;
1448         }
1449 #if 1
1450       printf(" fetching]\n"); fflush(stdout);
1451 #endif
1452       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1453       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1454       if (!descrdir)
1455         descrdir = "suse/setup/descr";
1456       filechksumtype = 0;
1457       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1458       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1459         return 0;
1460       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1461       fclose(fp);
1462       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1463       return 1;
1464     }
1465
1466   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1467   if (repomdtype)
1468     {
1469       if (!strcmp(repomdtype, "filelists"))
1470         strcpy(ext, "FL");
1471       else if (!strcmp(repomdtype, "deltainfo"))
1472         strcpy(ext, "DL");
1473       else
1474         return 0;
1475 #if 1
1476       printf("[%s:%s", data->repo->name, ext);
1477 #endif
1478       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1479         {
1480           printf(" cached]\n");fflush(stdout);
1481           return 1;
1482         }
1483       printf(" fetching]\n"); fflush(stdout);
1484       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1485       filechksumtype = 0;
1486       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1487       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1488         return 0;
1489       if (!strcmp(ext, "FL"))
1490         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1491       else if (!strcmp(ext, "DL"))
1492         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1493       fclose(fp);
1494       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1495       return 1;
1496     }
1497
1498   return 0;
1499 }
1500
1501 static unsigned char installedcookie[32];
1502
1503 #ifdef DEBIAN
1504 void
1505 repo_add_debdb(Repo *repo, int flags)
1506 {
1507   FILE *fp;
1508   if ((fp = fopen("/var/lib/dpkg/status", "r")) == 0)
1509     {
1510       perror("/var/lib/dpkg/status");
1511       exit(1);
1512     }
1513   repo_add_debpackages(repo, fp, flags);
1514   fclose(fp);
1515 }
1516
1517 static int
1518 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1519 {
1520   int i;
1521   for (i = 0; i < buflen; i++) 
1522     {    
1523 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
1524                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
1525                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
1526                 : -1)
1527       int v = c2h(*str);
1528       str++;
1529       if (v < 0) 
1530         return 0;
1531       buf[i] = v; 
1532       v = c2h(*str);
1533       str++;
1534       if (v < 0) 
1535         return 0;
1536       buf[i] = (buf[i] << 4) | v; 
1537 #undef c2h
1538     }    
1539   return buflen;
1540 }
1541
1542 const char *
1543 debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
1544 {
1545   char buf[4096];
1546   Id chksumtype;
1547   unsigned char *chksum;
1548   Id curchksumtype;
1549   int l, compl;
1550   char *ch, *fn, *bp;
1551   char *filename;
1552   static char *basearch;
1553   char *binarydir;
1554   int lbinarydir;
1555
1556   if (!basearch)
1557     {
1558       struct utsname un;
1559       if (uname(&un))
1560         {
1561           perror("uname");
1562           exit(1);
1563         }
1564       basearch = strdup(un.machine);
1565       if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
1566         basearch[1] = '3';
1567     }
1568   binarydir = sat_dupjoin("binary-", basearch, "/");
1569   lbinarydir = strlen(binarydir);
1570   compl = strlen(comp);
1571   rewind(fp);
1572   curchksumtype = 0;
1573   filename = 0;
1574   chksum = sat_malloc(32);
1575   chksumtype = 0;
1576   while(fgets(buf, sizeof(buf), fp))
1577     {
1578       l = strlen(buf);
1579       if (l == 0)
1580         continue;
1581       while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
1582         buf[--l] = 0;
1583       if (!strncasecmp(buf, "MD5Sum:", 7))
1584         {
1585           curchksumtype = REPOKEY_TYPE_MD5;
1586           continue;
1587         }
1588       if (!strncasecmp(buf, "SHA1:", 5))
1589         {
1590           curchksumtype = REPOKEY_TYPE_SHA1;
1591           continue;
1592         }
1593       if (!strncasecmp(buf, "SHA256:", 7))
1594         {
1595           curchksumtype = REPOKEY_TYPE_SHA256;
1596           continue;
1597         }
1598       if (!curchksumtype)
1599         continue;
1600       bp = buf;
1601       if (*bp++ != ' ')
1602         {
1603           curchksumtype = 0;
1604           continue;
1605         }
1606       ch = bp;
1607       while (*bp && *bp != ' ' && *bp != '\t')
1608         bp++;
1609       if (!*bp)
1610         continue;
1611       *bp++ = 0;
1612       while (*bp == ' ' || *bp == '\t')
1613         bp++;
1614       while (*bp && *bp != ' ' && *bp != '\t')
1615         bp++;
1616       if (!*bp)
1617         continue;
1618       while (*bp == ' ' || *bp == '\t')
1619         bp++;
1620       fn = bp;
1621       if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
1622         continue;
1623       bp += compl + 1;
1624       if (strncmp(bp, binarydir, lbinarydir))
1625         continue;
1626       bp += lbinarydir;
1627       if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
1628         {
1629           if (filename && !strcmp(bp, "Packages"))
1630             continue;
1631           if (chksumtype && sat_chksum_len(chksumtype) > sat_chksum_len(curchksumtype))
1632             continue;
1633           if (!hexstr2bytes(chksum, ch, sat_chksum_len(curchksumtype)))
1634             continue;
1635           sat_free(filename);
1636           filename = strdup(fn);
1637           chksumtype = curchksumtype;
1638         }
1639     }
1640   free(binarydir);
1641   if (filename)
1642     {
1643       fn = sat_dupjoin("/", filename, 0);
1644       sat_free(filename);
1645       filename = sat_dupjoin("dists/", cinfo->name, fn);
1646       sat_free(fn);
1647     }
1648   if (!chksumtype)
1649     chksum = sat_free(chksum);
1650   *chksump = chksum;
1651   *chksumtypep = chksumtype;
1652   return filename;
1653 }
1654 #endif
1655
1656 void
1657 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1658 {
1659   Repo *repo;
1660   struct repoinfo *cinfo;
1661   int i;
1662   FILE *fp;
1663   FILE *sigfp;
1664   const char *filename;
1665   const unsigned char *filechksum;
1666   Id filechksumtype;
1667   const char *descrdir;
1668   int defvendor;
1669   struct stat stb;
1670   Pool *sigpool = 0;
1671   Repodata *data;
1672   int badchecksum;
1673   int dorefresh;
1674 #ifdef DEBIAN
1675   FILE *fpr;
1676   int j;
1677 #endif
1678
1679   repo = repo_create(pool, "@System");
1680 #ifndef DEBIAN
1681   printf("rpm database:");
1682   if (stat("/var/lib/rpm/Packages", &stb))
1683     memset(&stb, 0, sizeof(&stb));
1684 #else
1685   printf("dpgk database:");
1686   if (stat("/var/lib/dpkg/status", &stb))
1687     memset(&stb, 0, sizeof(&stb));
1688 #endif
1689   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1690   if (usecachedrepo(repo, 0, installedcookie, 0))
1691     printf(" cached\n");
1692   else
1693     {
1694 #ifndef DEBIAN
1695       FILE *ofp;
1696       int done = 0;
1697 #endif
1698       printf(" reading\n");
1699
1700 #ifdef PRODUCTS_PATH
1701       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1702 #endif
1703 #ifndef DEBIAN
1704       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1705         {
1706           Repo *ref = repo_create(pool, "@System.old");
1707           if (!repo_add_solv(ref, ofp))
1708             {
1709               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1710               done = 1;
1711             }
1712           fclose(ofp);
1713           repo_free(ref, 1);
1714         }
1715       if (!done)
1716         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1717 #else
1718         repo_add_debdb(repo, REPO_REUSE_REPODATA);
1719 #endif
1720       writecachedrepo(repo, 0, 0, installedcookie);
1721     }
1722   pool_set_installed(pool, repo);
1723
1724   for (i = 0; i < nrepoinfos; i++)
1725     {
1726       cinfo = repoinfos + i;
1727       if (!cinfo->enabled)
1728         continue;
1729
1730       repo = repo_create(pool, cinfo->alias);
1731       cinfo->repo = repo;
1732       repo->appdata = cinfo;
1733       repo->priority = 99 - cinfo->priority;
1734
1735       dorefresh = cinfo->autorefresh;
1736       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1737         {
1738           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1739             dorefresh = 0;
1740         }
1741       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1742         {
1743           printf("repo '%s':", cinfo->alias);
1744           printf(" cached\n");
1745           continue;
1746         }
1747       badchecksum = 0;
1748       switch (cinfo->type)
1749         {
1750         case TYPE_RPMMD:
1751           printf("rpmmd repo '%s':", cinfo->alias);
1752           fflush(stdout);
1753           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1754             {
1755               printf(" no repomd.xml file, skipped\n");
1756               repo_free(repo, 1);
1757               cinfo->repo = 0;
1758               break;
1759             }
1760           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1761           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1762             {
1763               printf(" cached\n");
1764               fclose(fp);
1765               break;
1766             }
1767           if (cinfo->repo_gpgcheck)
1768             {
1769               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1770               if (!sigfp)
1771                 {
1772                   printf(" unsigned, skipped\n");
1773                   fclose(fp);
1774                   break;
1775                 }
1776               if (!sigpool)
1777                 sigpool = read_sigs();
1778               if (!checksig(sigpool, fp, sigfp))
1779                 {
1780                   printf(" checksig failed, skipped\n");
1781                   fclose(sigfp);
1782                   fclose(fp);
1783                   break;
1784                 }
1785               fclose(sigfp);
1786             }
1787           repo_add_repomdxml(repo, fp, 0);
1788           fclose(fp);
1789           printf(" fetching\n");
1790           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1791           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1792             {
1793               repo_add_rpmmd(repo, fp, 0, 0);
1794               fclose(fp);
1795             }
1796           if (badchecksum)
1797             break;      /* hopeless */
1798
1799           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1800           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1801             {
1802               repo_add_updateinfoxml(repo, fp, 0);
1803               fclose(fp);
1804             }
1805
1806           data = repo_add_repodata(repo, 0);
1807           if (!repomd_add_ext(repo, data, "deltainfo"))
1808             repomd_add_ext(repo, data, "prestodelta");
1809           repomd_add_ext(repo, data, "filelists");
1810           repodata_internalize(data);
1811           if (!badchecksum)
1812             writecachedrepo(repo, 0, 0, cinfo->cookie);
1813           repodata_create_stubs(repo_last_repodata(repo));
1814           break;
1815
1816         case TYPE_SUSETAGS:
1817           printf("susetags repo '%s':", cinfo->alias);
1818           fflush(stdout);
1819           descrdir = 0;
1820           defvendor = 0;
1821           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1822             {
1823               printf(" no content file, skipped\n");
1824               repo_free(repo, 1);
1825               cinfo->repo = 0;
1826               break;
1827             }
1828           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1829           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1830             {
1831               printf(" cached\n");
1832               fclose(fp);
1833               break;
1834             }
1835           if (cinfo->repo_gpgcheck)
1836             {
1837               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1838               if (!sigfp)
1839                 {
1840                   printf(" unsigned, skipped\n");
1841                   fclose(fp);
1842                   break;
1843                 }
1844               if (sigfp)
1845                 {
1846                   if (!sigpool)
1847                     sigpool = read_sigs();
1848                   if (!checksig(sigpool, fp, sigfp))
1849                     {
1850                       printf(" checksig failed, skipped\n");
1851                       fclose(sigfp);
1852                       fclose(fp);
1853                       break;
1854                     }
1855                   fclose(sigfp);
1856                 }
1857             }
1858           repo_add_content(repo, fp, 0);
1859           fclose(fp);
1860           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1861           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1862           if (!descrdir)
1863             descrdir = "suse/setup/descr";
1864           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1865           if (!filename)
1866             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1867           if (!filename)
1868             {
1869               printf(" no packages file entry, skipped\n");
1870               break;
1871             }
1872           printf(" fetching\n");
1873           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1874             break;      /* hopeless */
1875           repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE);
1876           fclose(fp);
1877           /* add default language */
1878           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1879           if (!filename)
1880             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1881           if (filename)
1882             {
1883               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1884                 {
1885                   repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES);
1886                   fclose(fp);
1887                 }
1888             }
1889           repo_internalize(repo);
1890           data = repo_add_repodata(repo, 0);
1891           susetags_add_ext(repo, data);
1892           repodata_internalize(data);
1893           if (!badchecksum)
1894             writecachedrepo(repo, 0, 0, cinfo->cookie);
1895           repodata_create_stubs(repo_last_repodata(repo));
1896           break;
1897
1898 #ifdef DEBIAN
1899         case TYPE_DEBIAN:
1900           printf("debian repo '%s':", cinfo->alias);
1901           fflush(stdout);
1902           filename = sat_dupjoin("dists/", cinfo->name, "/Release");
1903           if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
1904             {
1905               printf(" no Release file, skipped\n");
1906               repo_free(repo, 1);
1907               cinfo->repo = 0;
1908               free((char *)filename);
1909               break;
1910             }
1911           sat_free((char *)filename);
1912           if (cinfo->repo_gpgcheck)
1913             {
1914               filename = sat_dupjoin("dists/", cinfo->name, "/Release.gpg");
1915               sigfp = curlfopen(cinfo, filename, 0, 0, 0, 0);
1916               sat_free((char *)filename);
1917               if (!sigfp)
1918                 {
1919                   printf(" unsigned, skipped\n");
1920                   fclose(fpr);
1921                   break;
1922                 }
1923               if (!sigpool)
1924                 sigpool = read_sigs();
1925               if (!checksig(sigpool, fpr, sigfp))
1926                 {
1927                   printf(" checksig failed, skipped\n");
1928                   fclose(sigfp);
1929                   fclose(fpr);
1930                   break;
1931                 }
1932               fclose(sigfp);
1933             }
1934           calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
1935           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1936             {
1937               printf(" cached\n");
1938               fclose(fpr);
1939               break;
1940             }
1941           printf(" fetching\n");
1942           for (j = 0; j < cinfo->ncomponents; j++)
1943             {
1944               if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
1945                 {
1946                   printf("[component %s not found]\n", cinfo->components[j]);
1947                   continue;
1948                 }
1949               if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1950                 {
1951                   repo_add_debpackages(repo, fp, 0);
1952                   fclose(fp);
1953                 }
1954               sat_free((char *)filechksum);
1955               sat_free((char *)filename);
1956             }
1957           fclose(fpr);
1958           if (!badchecksum)
1959             writecachedrepo(repo, 0, 0, cinfo->cookie);
1960           break;
1961 #endif
1962
1963         default:
1964           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1965           repo_free(repo, 1);
1966           cinfo->repo = 0;
1967           break;
1968         }
1969     }
1970   if (sigpool)
1971     pool_free(sigpool);
1972 }
1973
1974
1975 int
1976 str2archid(Pool *pool, char *arch)
1977 {
1978   Id id;
1979   if (!*arch)
1980     return 0;
1981   id = str2id(pool, arch, 0);
1982   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1983     return id;
1984   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1985     return 0;
1986   return id;
1987 }
1988
1989
1990 #define DEPGLOB_NAME     1
1991 #define DEPGLOB_DEP      2
1992 #define DEPGLOB_NAMEDEP  3
1993
1994 int
1995 depglob(Pool *pool, char *name, Queue *job, int what)
1996 {
1997   Id p, pp;
1998   Id id = str2id(pool, name, 0);
1999   int i, match = 0;
2000
2001   if (id)
2002     {
2003       FOR_PROVIDES(p, pp, id)
2004         {
2005           Solvable *s = pool->solvables + p;
2006           match = 1;
2007           if (s->name == id && (what & DEPGLOB_NAME) != 0)
2008             {
2009               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
2010               return 1;
2011             }
2012         }
2013       if (match)
2014         {
2015           if (what == DEPGLOB_NAMEDEP)
2016             printf("[using capability match for '%s']\n", name);
2017           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
2018           return 1;
2019         }
2020     }
2021
2022   if (strpbrk(name, "[*?") == 0)
2023     return 0;
2024
2025   if ((what & DEPGLOB_NAME) != 0)
2026     {
2027       /* looks like a name glob. hard work. */
2028       for (p = 1; p < pool->nsolvables; p++)
2029         {
2030           Solvable *s = pool->solvables + p;
2031           if (!s->repo || !pool_installable(pool, s))
2032             continue;
2033           id = s->name;
2034           if (fnmatch(name, id2str(pool, id), 0) == 0)
2035             {
2036               for (i = 0; i < job->count; i += 2)
2037                 if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
2038                   break;
2039               if (i == job->count)
2040                 queue_push2(job, SOLVER_SOLVABLE_NAME, id);
2041               match = 1;
2042             }
2043         }
2044       if (match)
2045         return 1;
2046     }
2047   if ((what & DEPGLOB_DEP))
2048     {
2049       /* looks like a dep glob. really hard work. */
2050       for (id = 1; id < pool->ss.nstrings; id++)
2051         {
2052           if (!pool->whatprovides[id])
2053             continue;
2054           if (fnmatch(name, id2str(pool, id), 0) == 0)
2055             {
2056               if (!match && what == DEPGLOB_NAMEDEP)
2057                 printf("[using capability match for '%s']\n", name);
2058               for (i = 0; i < job->count; i += 2)
2059                 if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
2060                   break;
2061               if (i == job->count)
2062                 queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
2063               match = 1;
2064             }
2065         }
2066       if (match)
2067         return 1;
2068     }
2069   return 0;
2070 }
2071
2072 int
2073 limitrelation(Pool *pool, Queue *job, int flags, Id evr)
2074 {
2075   int i, j;
2076   Id p, pp;
2077   for (i = j = 0; i < job->count; i += 2)
2078     {
2079       Id select = job->elements[i] & SOLVER_SELECTMASK;
2080       if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
2081         {
2082           fprintf(stderr, "limitrelation only works on name/provides jobs\n");
2083           exit(1);
2084         }
2085       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
2086       if (flags == REL_ARCH)
2087         job->elements[i] |= SOLVER_SETARCH;
2088       if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && job->elements[i])
2089         {
2090 #ifndef DEBIAN
2091           const char *evrstr = id2str(pool, evr);
2092           if (!strchr(evrstr, '-'))
2093             job->elements[i] |= SOLVER_SETEV;
2094           else
2095 #endif
2096             job->elements[i] |= SOLVER_SETEVR;
2097         }
2098       /* make sure we still have matches */
2099       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2100         break;
2101       if (p)
2102         {
2103           job->elements[j] = job->elements[i];
2104           job->elements[j + 1] = job->elements[i + 1];
2105           j += 2;
2106         }
2107     }
2108   queue_truncate(job, j);
2109   return j / 2;
2110 }
2111
2112 int
2113 limitrelation_arch(Pool *pool, Queue *job, int flags, char *evr)
2114 {
2115   char *r;
2116   Id archid;
2117   if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2118     {
2119       *r = 0;
2120       limitrelation(pool, job, REL_ARCH, archid);
2121       limitrelation(pool, job, flags, str2id(pool, evr, 1));
2122       *r = '.';
2123     }
2124   else
2125     limitrelation(pool, job, flags, str2id(pool, evr, 1));
2126   return job->count / 2;
2127 }
2128
2129 int
2130 limitrepo(Pool *pool, Id repofilter, Queue *job)
2131 {
2132   Queue mq;
2133   Id p, pp;
2134   int i, j;
2135   Solvable *s;
2136
2137   queue_init(&mq);
2138   for (i = j = 0; i < job->count; i += 2)
2139     {
2140       queue_empty(&mq);
2141       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2142         {
2143           s = pool_id2solvable(pool, p);
2144           if (s->repo && s->repo->repoid == repofilter)
2145              queue_push(&mq, p);
2146         }
2147       if (mq.count)
2148         {
2149           /* here we assume that repo == vendor, so we also set SOLVER_SETVENDOR */
2150           if (mq.count == 1)
2151             {
2152               job->elements[j] = SOLVER_SOLVABLE | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_NOAUTOSET;
2153               job->elements[j + 1] = mq.elements[0];
2154             }
2155           else
2156             {
2157               job->elements[j] = SOLVER_SOLVABLE_ONE_OF | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO;
2158               job->elements[j + 1] = pool_queuetowhatprovides(pool, &mq);
2159             }
2160           j += 2;
2161         }
2162     }
2163   queue_truncate(job, j);
2164   queue_free(&mq);
2165   return j / 2;
2166 }
2167
2168 int
2169 mkselect(Pool *pool, int mode, char *name, Queue *job)
2170 {
2171   char *r, *r2;
2172   Id archid;
2173
2174   if (*name == '/')
2175     {
2176       Dataiterator di;
2177       Queue q;
2178
2179       queue_init(&q);
2180       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, SEARCH_STRING|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
2181       while (dataiterator_step(&di))
2182         {
2183           Solvable *s = pool->solvables + di.solvid;
2184           if (!s->repo || !pool_installable(pool, s))
2185             continue;
2186           queue_push(&q, di.solvid);
2187           dataiterator_skip_solvable(&di);
2188         }
2189       dataiterator_free(&di);
2190       if (q.count)
2191         {
2192           printf("[using file list match for '%s']\n", name);
2193           if (q.count > 1)
2194             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
2195           else
2196             queue_push2(job, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
2197           queue_free(&q);
2198           return job->count / 2;
2199         }
2200     }
2201   if ((r = strpbrk(name, "<=>")) != 0)
2202     {
2203       /* relation case, support:
2204        * depglob rel
2205        * depglob.arch rel
2206        */
2207       int rflags = 0;
2208       int nend = r - name;
2209       char oldnend;
2210       for (; *r; r++)
2211         {
2212           if (*r == '<')
2213             rflags |= REL_LT;
2214           else if (*r == '=')
2215             rflags |= REL_EQ;
2216           else if (*r == '>')
2217             rflags |= REL_GT;
2218           else
2219             break;
2220         }
2221       while (*r && *r == ' ' && *r == '\t')
2222         r++;
2223       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
2224         nend--;
2225       if (!*name || !*r)
2226         {
2227           fprintf(stderr, "bad relation\n");
2228           exit(1);
2229         }
2230       oldnend = name[nend];
2231       name[nend] = 0;
2232       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2233         {
2234           name[nend] = oldnend;
2235           limitrelation(pool, job, rflags, str2id(pool, r, 1));
2236           return job->count / 2;
2237         }
2238       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
2239         {
2240           *r2 = 0;
2241           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2242             {
2243               name[nend] = oldnend;
2244               *r2 = '.';
2245               limitrelation(pool, job, REL_ARCH, archid);
2246               limitrelation(pool, job, rflags, str2id(pool, r, 1));
2247               return job->count / 2;
2248             }
2249           *r2 = '.';
2250         }
2251       name[nend] = oldnend;
2252     }
2253   else
2254     {
2255       /* no relation case, support:
2256        * depglob
2257        * depglob.arch
2258        * nameglob-version
2259        * nameglob-version.arch
2260        * nameglob-version-release
2261        * nameglob-version-release.arch
2262        */
2263       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2264         return job->count / 2;
2265       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2266         {
2267           *r = 0;
2268           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2269             {
2270               *r = '.';
2271               limitrelation(pool, job, REL_ARCH, archid);
2272               return job->count / 2;
2273             }
2274           *r = '.';
2275         }
2276       if ((r = strrchr(name, '-')) != 0)
2277         {
2278           *r = 0;
2279           if (depglob(pool, name, job, DEPGLOB_NAME))
2280             {
2281               /* have just the version */
2282               limitrelation_arch(pool, job, REL_EQ, r + 1);
2283               *r = '-';
2284               return job->count / 2;
2285             }
2286           if ((r2 = strrchr(name, '-')) != 0)
2287             {
2288               *r = '-';
2289               *r2 = 0;
2290               r = r2;
2291               if (depglob(pool, name, job, DEPGLOB_NAME))
2292                 {
2293                   /* have version-release */
2294                   limitrelation_arch(pool, job, REL_EQ, r + 1);
2295                   *r = '-';
2296                   return job->count / 2;
2297                 }
2298             }
2299           *r = '-';
2300         }
2301     }
2302   return 0;
2303 }
2304
2305
2306 int
2307 yesno(const char *str)
2308 {
2309   char inbuf[128], *ip;
2310
2311   for (;;)
2312     {
2313       printf("%s", str);
2314       fflush(stdout);
2315       *inbuf = 0;
2316       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2317         {
2318           printf("Abort.\n");
2319           exit(1);
2320         }
2321       while (*ip == ' ' || *ip == '\t')
2322         ip++;
2323       if (*ip == 'q')
2324         {
2325           printf("Abort.\n");
2326           exit(1);
2327         }
2328       if (*ip == 'y' || *ip == 'n')
2329         return *ip == 'y' ? 1 : 0;
2330     }
2331 }
2332
2333 #ifndef DEBIAN
2334
2335 struct fcstate {
2336   FILE **newpkgsfps;
2337   Queue *checkq;
2338   int newpkgscnt;
2339   void *rpmdbstate;
2340 };
2341
2342 static void *
2343 fileconflict_cb(Pool *pool, Id p, void *cbdata)
2344 {
2345   struct fcstate *fcstate = cbdata;
2346   Solvable *s;
2347   Id rpmdbid;
2348   int i;
2349   FILE *fp;
2350
2351   if (!p)
2352     {
2353       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
2354       return 0;
2355     }
2356   s = pool_id2solvable(pool, p);
2357   if (pool->installed && s->repo == pool->installed)
2358     {
2359       if (!s->repo->rpmdbid)
2360         return 0;
2361       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
2362       if (!rpmdbid)
2363         return 0;
2364        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
2365     }
2366   for (i = 0; i < fcstate->newpkgscnt; i++)
2367     if (fcstate->checkq->elements[i] == p)
2368       break;
2369   if (i == fcstate->newpkgscnt)
2370     return 0;
2371   fp = fcstate->newpkgsfps[i];
2372   if (!fp)
2373     return 0;
2374   rewind(fp);
2375   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
2376 }
2377
2378
2379 void
2380 runrpm(const char *arg, const char *name, int dupfd3)
2381 {
2382   pid_t pid;
2383   int status;
2384
2385   if ((pid = fork()) == (pid_t)-1)
2386     {
2387       perror("fork");
2388       exit(1);
2389     }
2390   if (pid == 0)
2391     {
2392       if (dupfd3 != -1 && dupfd3 != 3)
2393         {
2394           dup2(dupfd3, 3);
2395           close(dupfd3);
2396         }
2397       if (dupfd3 != -1)
2398         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2399       if (strcmp(arg, "-e") == 0)
2400         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2401       else
2402         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2403       perror("rpm");
2404       _exit(0);
2405     }
2406   while (waitpid(pid, &status, 0) != pid)
2407     ;
2408   if (status)
2409     {
2410       printf("rpm failed\n");
2411       exit(1);
2412     }
2413 }
2414
2415 #endif
2416
2417 #ifdef DEBIAN
2418
2419 void
2420 rundpkg(const char *arg, const char *name, int dupfd3)
2421 {
2422   pid_t pid;
2423   int status;
2424
2425   if ((pid = fork()) == (pid_t)-1)
2426     {
2427       perror("fork");
2428       exit(1);
2429     }
2430   if (pid == 0)
2431     {
2432       if (dupfd3 != -1 && dupfd3 != 3)
2433         {
2434           dup2(dupfd3, 3);
2435           close(dupfd3);
2436         }
2437       if (dupfd3 != -1)
2438         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2439       if (strcmp(arg, "--install") == 0)
2440         execlp("dpkg", "dpkg", "--install", "--force", "all", name, (char *)0);
2441       else
2442         execlp("dpkg", "dpkg", "--remove", "--force", "all", name, (char *)0);
2443       perror("dpkg");
2444       _exit(0);
2445     }
2446   while (waitpid(pid, &status, 0) != pid)
2447     ;
2448   if (status)
2449     {
2450       printf("dpkg failed\n");
2451       exit(1);
2452     }
2453 }
2454
2455 #endif
2456
2457 static Id
2458 nscallback(Pool *pool, void *data, Id name, Id evr)
2459 {
2460   if (name == NAMESPACE_PRODUCTBUDDY)
2461     {    
2462       /* SUSE specific hack: each product has an associated rpm */
2463       Solvable *s = pool->solvables + evr; 
2464       Id p, pp, cap; 
2465       
2466       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
2467       if (!cap)
2468         return 0;
2469       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
2470       if (!cap)
2471         return 0;
2472       FOR_PROVIDES(p, pp, cap) 
2473         {
2474           Solvable *ps = pool->solvables + p; 
2475           if (ps->repo == s->repo && ps->arch == s->arch)
2476             break;
2477         }
2478       return p;
2479     }
2480   return 0;
2481 }
2482
2483 #ifdef SOFTLOCKS_PATH
2484
2485 void
2486 addsoftlocks(Pool *pool, Queue *job)
2487 {
2488   FILE *fp;
2489   Id type, id, p, pp;
2490   char *bp, *ep, buf[4096];
2491
2492   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2493     return;
2494   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2495     {
2496       while (*bp == ' ' || *bp == '\t')
2497         bp++;
2498       if (!*bp || *bp == '#')
2499         continue;
2500       for (ep = bp; *ep; ep++)
2501         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2502           break;
2503       *ep = 0;
2504       type = SOLVER_SOLVABLE_NAME;
2505       if (!strncmp(bp, "provides:", 9) && bp[9])
2506         {
2507           type = SOLVER_SOLVABLE_PROVIDES;
2508           bp += 9;
2509         }
2510       id = str2id(pool, bp, 1);
2511       if (pool->installed)
2512         {
2513           FOR_JOB_SELECT(p, pp, type, id)
2514             if (pool->solvables[p].repo == pool->installed)
2515               break;
2516           if (p)
2517             continue;   /* ignore, as it is already installed */
2518         }
2519       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2520     }
2521   fclose(fp);
2522 }
2523
2524 #endif
2525
2526
2527 void
2528 rewrite_repos(Pool *pool, Id *addedfileprovides)
2529 {
2530   Repo *repo;
2531   Repodata *data;
2532   Map providedids;
2533   Queue fileprovidesq;
2534   Id id;
2535   int i, j, n, nprovidedids;
2536   struct repoinfo *cinfo;
2537
2538   map_init(&providedids, pool->ss.nstrings);
2539   queue_init(&fileprovidesq);
2540   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2541     MAPSET(&providedids, id);
2542   FOR_REPOS(i, repo)
2543     {
2544       /* make sure this repo has just one main repodata */
2545       if (!repo->nrepodata)
2546         continue;
2547       cinfo = repo->appdata;
2548       data = repo->repodata + 0;
2549       if (data->store.pagefd == -1)
2550         continue;
2551       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2552         {
2553           n = 0;
2554           for (j = 0; j < fileprovidesq.count; j++)
2555             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2556               n++;
2557           if (n == nprovidedids)
2558             continue;   /* nothing new added */
2559         }
2560       /* oh my! */
2561       for (j = 0; addedfileprovides[j]; j++)
2562         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2563       repodata_internalize(data);
2564       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2565     }
2566   queue_free(&fileprovidesq);
2567   map_free(&providedids);
2568 }
2569
2570 #define MODE_LIST        0
2571 #define MODE_INSTALL     1
2572 #define MODE_ERASE       2
2573 #define MODE_UPDATE      3
2574 #define MODE_DISTUPGRADE 4
2575 #define MODE_VERIFY      5
2576 #define MODE_PATCH       6
2577 #define MODE_INFO        7
2578 #define MODE_REPOLIST    8
2579 #define MODE_SEARCH      9
2580 #define MODE_ERASECLEAN  10
2581
2582 void
2583 usage(int r)
2584 {
2585   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2586   fprintf(stderr, "\n");
2587   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2588   fprintf(stderr, "                  versions from the repositories\n");
2589   fprintf(stderr, "    erase:        erase installed packages\n");
2590   fprintf(stderr, "    info:         display package information\n");
2591   fprintf(stderr, "    install:      install packages\n");
2592   fprintf(stderr, "    list:         list packages\n");
2593   fprintf(stderr, "    repos:        list enabled repositories\n");
2594   fprintf(stderr, "    search:       search name/summary/description\n");
2595   fprintf(stderr, "    update:       update installed packages\n");
2596   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2597   fprintf(stderr, "\n");
2598   exit(r);
2599 }
2600
2601 int
2602 main(int argc, char **argv)
2603 {
2604   Pool *pool;
2605   Repo *commandlinerepo = 0;
2606   Id *commandlinepkgs = 0;
2607   Id p, pp;
2608   struct repoinfo *repoinfos;
2609   int nrepoinfos = 0;
2610   int mainmode = 0, mode = 0;
2611   int i, newpkgs;
2612   Queue job, checkq;
2613   Solver *solv = 0;
2614   Transaction *trans;
2615   char inbuf[128], *ip;
2616   int allpkgs = 0;
2617   FILE **newpkgsfps;
2618   Id *addedfileprovides = 0;
2619   Id repofilter = 0;
2620
2621   argc--;
2622   argv++;
2623   if (!argv[0])
2624     usage(1);
2625   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2626     {
2627       mainmode = MODE_INSTALL;
2628       mode = SOLVER_INSTALL;
2629     }
2630   else if (!strcmp(argv[0], "patch"))
2631     {
2632       mainmode = MODE_PATCH;
2633       mode = SOLVER_INSTALL;
2634     }
2635   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2636     {
2637       mainmode = MODE_ERASE;
2638       mode = SOLVER_ERASE;
2639     }
2640   else if (!strcmp(argv[0], "eraseclean") || !strcmp(argv[0], "rmclean"))
2641     {
2642       mainmode = MODE_ERASECLEAN;
2643       mode = SOLVER_ERASE;
2644     }
2645   else if (!strcmp(argv[0], "list"))
2646     {
2647       mainmode = MODE_LIST;
2648       mode = 0;
2649     }
2650   else if (!strcmp(argv[0], "info"))
2651     {
2652       mainmode = MODE_INFO;
2653       mode = 0;
2654     }
2655   else if (!strcmp(argv[0], "search"))
2656     {
2657       mainmode = MODE_SEARCH;
2658       mode = 0;
2659     }
2660   else if (!strcmp(argv[0], "verify"))
2661     {
2662       mainmode = MODE_VERIFY;
2663       mode = SOLVER_VERIFY;
2664     }
2665   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2666     {
2667       mainmode = MODE_UPDATE;
2668       mode = SOLVER_UPDATE;
2669     }
2670   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2671     {
2672       mainmode = MODE_DISTUPGRADE;
2673       mode = SOLVER_UPDATE;
2674     }
2675   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2676     {
2677       mainmode = MODE_REPOLIST;
2678       mode = 0;
2679     }
2680   else
2681     usage(1);
2682
2683   pool = pool_create();
2684
2685 #if 0
2686   {
2687     const char *langs[] = {"de_DE", "de", "en"};
2688     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2689   }
2690 #endif
2691
2692 #ifdef FEDORA
2693   pool->obsoleteusescolors = 1;
2694 #endif
2695   pool_setloadcallback(pool, load_stub, 0);
2696   pool->nscallback = nscallback;
2697   // pool_setdebuglevel(pool, 2);
2698   setarch(pool);
2699   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2700
2701   if (mainmode == MODE_REPOLIST)
2702     {
2703       int j = 1;
2704       for (i = 0; i < nrepoinfos; i++)
2705         {
2706           struct repoinfo *cinfo = repoinfos + i;
2707           if (!cinfo->enabled)
2708             continue;
2709           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2710         }
2711       exit(0);
2712     }
2713
2714   read_repos(pool, repoinfos, nrepoinfos);
2715
2716   if (argc > 2 && !strcmp(argv[1], "-r"))
2717     {
2718       const char *rname = argv[2], *rp;
2719       for (rp = rname; *rp; rp++)
2720         if (*rp <= '0' || *rp >= '9')
2721           break;
2722       if (!*rp)
2723         {
2724           /* repo specified by number */
2725           int rnum = atoi(rname);
2726           for (i = 0; i < nrepoinfos; i++)
2727             {
2728               struct repoinfo *cinfo = repoinfos + i;
2729               if (!cinfo->enabled)
2730                 continue;
2731               if (--rnum == 0)
2732                 repofilter = cinfo->repo->repoid;
2733             }
2734         }
2735       else
2736         {
2737           /* repo specified by alias */
2738           Repo *repo;
2739           FOR_REPOS(i, repo)
2740             {
2741               if (!strcasecmp(rname, repo->name))
2742                 repofilter = repo->repoid;
2743             }
2744         }
2745       if (!repofilter)
2746         {
2747           fprintf(stderr, "%s: no such repo\n", rname);
2748           exit(1);
2749         }
2750       argc -= 2;
2751       argv += 2;
2752     }
2753   if (mainmode == MODE_SEARCH)
2754     {
2755       Dataiterator di;
2756       Map m;
2757       if (argc != 2)
2758         usage(1);
2759       map_init(&m, pool->nsolvables);
2760       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2761       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2762       dataiterator_set_search(&di, 0, 0);
2763       while (dataiterator_step(&di))
2764         MAPSET(&m, di.solvid);
2765       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2766       dataiterator_set_search(&di, 0, 0);
2767       while (dataiterator_step(&di))
2768         MAPSET(&m, di.solvid);
2769       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2770       dataiterator_set_search(&di, 0, 0);
2771       while (dataiterator_step(&di))
2772         MAPSET(&m, di.solvid);
2773       dataiterator_free(&di);
2774
2775       for (p = 1; p < pool->nsolvables; p++)
2776         {
2777           Solvable *s = pool_id2solvable(pool, p);
2778           if (!MAPTST(&m, p))
2779             continue;
2780           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2781         }
2782       map_free(&m);
2783       exit(0);
2784     }
2785
2786
2787   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2788     {
2789       for (i = 1; i < argc; i++)
2790         {
2791           int l;
2792           l = strlen(argv[i]);
2793 #ifndef DEBIAN
2794           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2795             continue;
2796 #else
2797           if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
2798             continue;
2799 #endif
2800           if (access(argv[i], R_OK))
2801             {
2802               perror(argv[i]);
2803               exit(1);
2804             }
2805           if (!commandlinepkgs)
2806             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2807           if (!commandlinerepo)
2808             commandlinerepo = repo_create(pool, "@commandline");
2809 #ifndef DEBIAN
2810           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2811 #else
2812           repo_add_debs(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2813 #endif
2814           commandlinepkgs[i] = commandlinerepo->end - 1;
2815         }
2816       if (commandlinerepo)
2817         repo_internalize(commandlinerepo);
2818     }
2819
2820   // FOR_REPOS(i, repo)
2821   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2822   addedfileprovides = 0;
2823   pool_addfileprovides_ids(pool, 0, &addedfileprovides);
2824   if (addedfileprovides && *addedfileprovides)
2825     rewrite_repos(pool, addedfileprovides);
2826   sat_free(addedfileprovides);
2827   pool_createwhatprovides(pool);
2828
2829   queue_init(&job);
2830   for (i = 1; i < argc; i++)
2831     {
2832       Queue job2;
2833       int j;
2834
2835       if (commandlinepkgs && commandlinepkgs[i])
2836         {
2837           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2838           continue;
2839         }
2840       queue_init(&job2);
2841       if (!mkselect(pool, mode, argv[i], &job2))
2842         {
2843           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
2844           exit(1);
2845         }
2846       if (repofilter && !limitrepo(pool, repofilter, &job2))
2847         {
2848           fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]);
2849           exit(1);
2850         }
2851       for (j = 0; j < job2.count; j++)
2852         queue_push(&job, job2.elements[j]);
2853       queue_free(&job2);
2854     }
2855
2856   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2857     {
2858       printf("no package matched\n");
2859       exit(1);
2860     }
2861
2862   if (!job.count)
2863     allpkgs = 1;
2864
2865   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2866     {
2867       /* list mode, no solver needed */
2868       for (i = 0; i < job.count; i += 2)
2869         {
2870           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2871             {
2872               Solvable *s = pool_id2solvable(pool, p);
2873               if (mainmode == MODE_INFO)
2874                 {
2875                   const char *str;
2876                   printf("Name:        %s\n", solvable2str(pool, s));
2877                   printf("Repo:        %s\n", s->repo->name);
2878                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2879                   str = solvable_lookup_str(s, SOLVABLE_URL);
2880                   if (str)
2881                     printf("Url:         %s\n", str);
2882                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
2883                   if (str)
2884                     printf("License:     %s\n", str);
2885                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2886                   printf("\n");
2887                 }
2888               else
2889                 {
2890 #if 1
2891                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
2892 #else
2893                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
2894 #endif
2895                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2896                   if (sum)
2897                     printf("    %s\n", sum);
2898                 }
2899             }
2900         }
2901       queue_free(&job);
2902       pool_free(pool);
2903       free_repoinfos(repoinfos, nrepoinfos);
2904       sat_free(commandlinepkgs);
2905 #ifdef FEDORA
2906       yum_substitute(pool, 0);
2907 #endif
2908       exit(0);
2909     }
2910
2911   if (mainmode == MODE_PATCH)
2912     {
2913       int pruneyou = 0;
2914       Map installedmap;
2915       Solvable *s;
2916
2917       map_init(&installedmap, pool->nsolvables);
2918       if (pool->installed)
2919         FOR_REPO_SOLVABLES(pool->installed, p, s)
2920           MAPSET(&installedmap, p);
2921
2922       /* install all patches */
2923       for (p = 1; p < pool->nsolvables; p++)
2924         {
2925           const char *type;
2926           int r;
2927           Id p2;
2928
2929           s = pool->solvables + p;
2930           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2931             continue;
2932           FOR_PROVIDES(p2, pp, s->name)
2933             {
2934               Solvable *s2 = pool->solvables + p2;
2935               if (s2->name != s->name)
2936                 continue;
2937               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2938               if (r < 0 || (r == 0 && p > p2))
2939                 break;
2940             }
2941           if (p2)
2942             continue;
2943           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2944           if (type && !strcmp(type, "optional"))
2945             continue;
2946           r = solvable_trivial_installable_map(s, &installedmap, 0);
2947           if (r == -1)
2948             continue;
2949           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2950             {
2951               if (!pruneyou++)
2952                 queue_empty(&job);
2953             }
2954           else if (pruneyou)
2955             continue;
2956           queue_push2(&job, SOLVER_SOLVABLE, p);
2957         }
2958       map_free(&installedmap);
2959     }
2960
2961   // add mode
2962   for (i = 0; i < job.count; i += 2)
2963     {
2964       if (mode == SOLVER_UPDATE)
2965         {
2966           /* make update of not installed packages an install */
2967           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2968             if (pool->installed && pool->solvables[p].repo == pool->installed)
2969               break;
2970           if (!p)
2971             {
2972               job.elements[i] |= SOLVER_INSTALL;
2973               continue;
2974             }
2975         }
2976       job.elements[i] |= mode;
2977       if (mainmode == MODE_ERASECLEAN)
2978         job.elements[i] |= SOLVER_CLEANDEPS;
2979     }
2980
2981   if (mainmode == MODE_DISTUPGRADE && allpkgs && repofilter)
2982     queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
2983
2984   // multiversion test
2985   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2986   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2987   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2988
2989 #ifdef SOFTLOCKS_PATH
2990   addsoftlocks(pool, &job);
2991 #endif
2992
2993 #ifndef DEBIAN
2994 rerunsolver:
2995 #endif
2996   for (;;)
2997     {
2998       Id problem, solution;
2999       int pcnt, scnt;
3000
3001       solv = solver_create(pool);
3002       solv->ignorealreadyrecommended = 1;
3003       solv->updatesystem = allpkgs && !repofilter && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
3004       solv->dosplitprovides = solv->updatesystem;
3005       solv->fixsystem = allpkgs && !repofilter && mainmode == MODE_VERIFY;
3006       if (mainmode == MODE_DISTUPGRADE && allpkgs && !repofilter)
3007         {
3008           solv->distupgrade = 1;
3009           solv->allowdowngrade = 1;
3010           solv->allowarchchange = 1;
3011           solv->allowvendorchange = 1;
3012         }
3013       if (mainmode == MODE_ERASE || mainmode == MODE_ERASECLEAN)
3014         solv->allowuninstall = 1;       /* don't nag */
3015
3016       solver_solve(solv, &job);
3017       if (!solv->problems.count)
3018         break;
3019       pcnt = solver_problem_count(solv);
3020       printf("Found %d problems:\n", pcnt);
3021       for (problem = 1; problem <= pcnt; problem++)
3022         {
3023           int take = 0;
3024           printf("Problem %d:\n", problem);
3025           solver_printprobleminfo(solv, problem);
3026           printf("\n");
3027           scnt = solver_solution_count(solv, problem);
3028           for (solution = 1; solution <= scnt; solution++)
3029             {
3030               printf("Solution %d:\n", solution);
3031               solver_printsolution(solv, problem, solution);
3032               printf("\n");
3033             }
3034           for (;;)
3035             {
3036               printf("Please choose a solution: ");
3037               fflush(stdout);
3038               *inbuf = 0;
3039               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
3040                 {
3041                   printf("Abort.\n");
3042                   exit(1);
3043                 }
3044               while (*ip == ' ' || *ip == '\t')
3045                 ip++;
3046               if (*ip >= '0' && *ip <= '9')
3047                 {
3048                   take = atoi(ip);
3049                   if (take >= 1 && take <= scnt)
3050                     break;
3051                 }
3052               if (*ip == 's')
3053                 {
3054                   take = 0;
3055                   break;
3056                 }
3057               if (*ip == 'q')
3058                 {
3059                   printf("Abort.\n");
3060                   exit(1);
3061                 }
3062             }
3063           if (!take)
3064             continue;
3065           solver_take_solution(solv, problem, take, &job);
3066         }
3067       solver_free(solv);
3068       solv = 0;
3069     }
3070
3071   trans = &solv->trans;
3072   if (!trans->steps.count)
3073     {
3074       printf("Nothing to do.\n");
3075       exit(1);
3076     }
3077   printf("\n");
3078   printf("Transaction summary:\n\n");
3079   solver_printtransaction(solv);
3080
3081 #if !defined(FEDORA) && !defined(DEBIAN)
3082   if (1)
3083     {
3084       DUChanges duc[4];
3085       int i;
3086
3087       duc[0].path = "/";
3088       duc[1].path = "/usr/share/man";
3089       duc[2].path = "/sbin";
3090       duc[3].path = "/etc";
3091       transaction_calc_duchanges(trans, duc, 4);
3092       for (i = 0; i < 4; i++)
3093         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
3094     }
3095 #endif
3096   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
3097   printf("\n");
3098
3099   if (!yesno("OK to continue (y/n)? "))
3100     {
3101       printf("Abort.\n");
3102       solver_free(solv);
3103       queue_free(&job);
3104       pool_free(pool);
3105       free_repoinfos(repoinfos, nrepoinfos);
3106       sat_free(commandlinepkgs);
3107 #ifdef FEDORA
3108       yum_substitute(pool, 0);
3109 #endif
3110       exit(1);
3111     }
3112
3113   queue_init(&checkq);
3114   newpkgs = transaction_installedresult(trans, &checkq);
3115   newpkgsfps = 0;
3116
3117   if (newpkgs)
3118     {
3119       int downloadsize = 0;
3120       for (i = 0; i < newpkgs; i++)
3121         {
3122           Solvable *s;
3123
3124           p = checkq.elements[i];
3125           s = pool_id2solvable(pool, p);
3126           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
3127         }
3128       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
3129       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
3130       for (i = 0; i < newpkgs; i++)
3131         {
3132           unsigned int medianr;
3133           char *loc;
3134           Solvable *s;
3135           struct repoinfo *cinfo;
3136           const unsigned char *chksum;
3137           Id chksumtype;
3138           Dataiterator di;
3139
3140           p = checkq.elements[i];
3141           s = pool_id2solvable(pool, p);
3142           if (s->repo == commandlinerepo)
3143             {
3144               loc = solvable_get_location(s, &medianr);
3145               if (!(newpkgsfps[i] = fopen(loc, "r")))
3146                 {
3147                   perror(loc);
3148                   exit(1);
3149                 }
3150               putchar('.');
3151               continue;
3152             }
3153           cinfo = s->repo->appdata;
3154           if (!cinfo)
3155             {
3156               printf("%s: no repository information\n", s->repo->name);
3157               exit(1);
3158             }
3159           loc = solvable_get_location(s, &medianr);
3160           if (!loc)
3161              continue;
3162
3163           if (pool->installed && pool->installed->nsolvables)
3164             {
3165               /* try a delta first */
3166               char *matchname = strdup(id2str(pool, s->name));
3167               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
3168               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
3169               while (dataiterator_step(&di))
3170                 {
3171                   Id baseevr, op;
3172
3173                   dataiterator_setpos_parent(&di);
3174                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
3175                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
3176                     continue;
3177                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
3178                   FOR_PROVIDES(op, pp, s->name)
3179                     {
3180                       Solvable *os = pool->solvables + op;
3181                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
3182                         break;
3183                     }
3184                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
3185                     {
3186                       /* base is installed, run sequence check */
3187                       const char *seqname;
3188                       const char *seqevr;
3189                       const char *seqnum;
3190                       const char *seq;
3191                       const char *dloc;
3192                       FILE *fp;
3193                       char cmd[128];
3194                       int newfd;
3195
3196                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
3197                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
3198                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
3199                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
3200                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
3201 #ifdef FEDORA
3202                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
3203 #else
3204                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
3205 #endif
3206                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
3207                         continue;       /* didn't match */
3208                       /* looks good, download delta */
3209                       chksumtype = 0;
3210                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
3211                       if (!chksumtype)
3212                         continue;       /* no way! */
3213                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
3214                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
3215                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
3216                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
3217                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
3218                         continue;
3219                       /* got it, now reconstruct */
3220                       newfd = opentmpfile();
3221 #ifdef FEDORA
3222                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
3223 #else
3224                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
3225 #endif
3226                       fcntl(fileno(fp), F_SETFD, 0);
3227                       if (system(cmd))
3228                         {
3229                           close(newfd);
3230                           fclose(fp);
3231                           continue;
3232                         }
3233                       lseek(newfd, 0, SEEK_SET);
3234                       chksumtype = 0;
3235                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3236                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
3237                         {
3238                           close(newfd);
3239                           fclose(fp);
3240                           continue;
3241                         }
3242                       newpkgsfps[i] = fdopen(newfd, "r");
3243                       fclose(fp);
3244                       break;
3245                     }
3246                 }
3247               dataiterator_free(&di);
3248               sat_free(matchname);
3249             }
3250           
3251           if (newpkgsfps[i])
3252             {
3253               putchar('d');
3254               fflush(stdout);
3255               continue;         /* delta worked! */
3256             }
3257           if (cinfo->type == TYPE_SUSETAGS)
3258             {
3259               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
3260               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
3261             }
3262           chksumtype = 0;
3263           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3264           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
3265             {
3266               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
3267               exit(1);
3268             }
3269           putchar('.');
3270           fflush(stdout);
3271         }
3272       putchar('\n');
3273     }
3274
3275 #ifndef DEBIAN
3276   if (newpkgs)
3277     {
3278       Queue conflicts;
3279       struct fcstate fcstate;
3280
3281       printf("Searching for file conflicts\n");
3282       queue_init(&conflicts);
3283       fcstate.rpmdbstate = 0;
3284       fcstate.newpkgscnt = newpkgs;
3285       fcstate.checkq = &checkq;
3286       fcstate.newpkgsfps = newpkgsfps;
3287       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
3288       if (conflicts.count)
3289         {
3290           printf("\n");
3291           for (i = 0; i < conflicts.count; i += 5)
3292             printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
3293           printf("\n");
3294           if (yesno("Re-run solver (y/n/q)? "))
3295             {
3296               for (i = 0; i < newpkgs; i++)
3297                 if (newpkgsfps[i])
3298                   fclose(newpkgsfps[i]);
3299               newpkgsfps = sat_free(newpkgsfps);
3300               solver_free(solv);
3301               pool_add_fileconflicts_deps(pool, &conflicts);
3302               pool_createwhatprovides(pool);    /* Hmm... */
3303               goto rerunsolver;
3304             }
3305         }
3306       queue_free(&conflicts);
3307     }
3308 #endif
3309
3310   printf("Committing transaction:\n\n");
3311   transaction_order(trans, 0);
3312   for (i = 0; i < trans->steps.count; i++)
3313     {
3314 #ifndef DEBIAN
3315       const char *evr, *evrp, *nvra;
3316 #endif
3317       Solvable *s;
3318       int j;
3319       FILE *fp;
3320
3321       p = trans->steps.elements[i];
3322       s = pool_id2solvable(pool, p);
3323       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
3324       switch(type)
3325         {
3326         case SOLVER_TRANSACTION_ERASE:
3327           printf("erase %s\n", solvid2str(pool, p));
3328 #ifndef DEBIAN
3329           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
3330             continue;
3331           /* strip epoch from evr */
3332           evr = evrp = id2str(pool, s->evr);
3333           while (*evrp >= '0' && *evrp <= '9')
3334             evrp++;
3335           if (evrp > evr && evrp[0] == ':' && evrp[1])
3336             evr = evrp + 1;
3337           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
3338           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
3339           runrpm("-e", nvra, -1);       /* too bad that --querybynumber doesn't work */
3340 #else
3341           rundpkg("--remove", id2str(pool, s->name), 0);
3342 #endif
3343           break;
3344         case SOLVER_TRANSACTION_INSTALL:
3345         case SOLVER_TRANSACTION_MULTIINSTALL:
3346           printf("install %s\n", solvid2str(pool, p));
3347           for (j = 0; j < newpkgs; j++)
3348             if (checkq.elements[j] == p)
3349               break;
3350           fp = j < newpkgs ? newpkgsfps[j] : 0;
3351           if (!fp)
3352             continue;
3353           rewind(fp);
3354           lseek(fileno(fp), 0, SEEK_SET);
3355 #ifndef DEBIAN
3356           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
3357 #else
3358           rundpkg("--install", "/dev/fd/3", fileno(fp));
3359 #endif
3360           fclose(fp);
3361           newpkgsfps[j] = 0;
3362           break;
3363         default:
3364           break;
3365         }
3366     }
3367
3368   for (i = 0; i < newpkgs; i++)
3369     if (newpkgsfps[i])
3370       fclose(newpkgsfps[i]);
3371   sat_free(newpkgsfps);
3372   queue_free(&checkq);
3373   solver_free(solv);
3374   queue_free(&job);
3375   pool_free(pool);
3376   free_repoinfos(repoinfos, nrepoinfos);
3377   sat_free(commandlinepkgs);
3378 #ifdef FEDORA
3379   yum_substitute(pool, 0);
3380 #endif
3381   exit(0);
3382 }