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