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