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