- use SUSETAGS_RECORD_SHARES feature so extensions can reuse the shared data
[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   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   Pool *pool = repo->pool;
1252   Dataiterator di;
1253   Id chksumtype, handle;
1254   const unsigned char *chksum;
1255   const char *filename;
1256
1257   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
1258   dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
1259   if (!dataiterator_step(&di))
1260     {
1261       dataiterator_free(&di);
1262       return 0;
1263     }
1264   if (!strcmp(what, "prestodelta"))
1265     what = "deltainfo";
1266   dataiterator_setpos_parent(&di);
1267   filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
1268   chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, &chksumtype);
1269   if (!filename || !chksum)
1270     {
1271       dataiterator_free(&di);
1272       return 0;
1273     }
1274   handle = repodata_new_handle(data);
1275   repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
1276   repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
1277   if (chksumtype)
1278     repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
1279   if (!strcmp(what, "deltainfo"))
1280     {
1281       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
1282       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
1283     }
1284   if (!strcmp(what, "filelists"))
1285     {
1286       repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1287       repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1288     }
1289   dataiterator_free(&di);
1290   repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1291   return 1;
1292 }
1293
1294
1295 /* susetags helpers */
1296
1297 static inline const char *
1298 susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
1299 {
1300   Pool *pool = repo->pool;
1301   Dataiterator di;
1302   const char *filename;
1303
1304   filename = 0;
1305   *chksump = 0;
1306   *chksumtypep = 0;
1307   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
1308   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1309   if (dataiterator_step(&di))
1310     {
1311       dataiterator_setpos_parent(&di);
1312       *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
1313       filename = what;
1314     }
1315   dataiterator_free(&di);
1316   if (filename && !*chksumtypep)
1317     {
1318       printf("no %s file checksum!\n", what);
1319       filename = 0;
1320     }
1321   return filename;
1322 }
1323
1324 static Id susetags_langtags[] = {
1325   SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
1326   SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
1327   SOLVABLE_EULA, REPOKEY_TYPE_STR,
1328   SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
1329   SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
1330   SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
1331   0, 0
1332 };
1333
1334 void
1335 susetags_add_ext(Repo *repo, Repodata *data)
1336 {
1337   Pool *pool = repo->pool;
1338   Dataiterator di;
1339   char ext[3];
1340   Id handle, filechksumtype;
1341   const unsigned char *filechksum;
1342   int i;
1343
1344   dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
1345   dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
1346   while (dataiterator_step(&di))
1347     {
1348       if (strncmp(di.kv.str, "packages.", 9) != 0)
1349         continue;
1350       if (!strcmp(di.kv.str + 9, "gz"))
1351         continue;
1352       if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
1353         continue;
1354       ext[0] = di.kv.str[9];
1355       ext[1] = di.kv.str[10];
1356       ext[2] = 0;
1357       if (!strcmp(ext, "en"))
1358         continue;
1359       if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
1360         continue;
1361       handle = repodata_new_handle(data);
1362       repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
1363       if (filechksumtype)
1364         repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
1365       if (!strcmp(ext, "DU"))
1366         {
1367           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
1368           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
1369         }
1370       else if (!strcmp(ext, "FL"))
1371         {
1372           repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
1373           repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
1374         }
1375       else
1376         {
1377           for (i = 0; susetags_langtags[i]; i += 2)
1378             {
1379               repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
1380               repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
1381             }
1382         }
1383       repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
1384     }
1385   dataiterator_free(&di);
1386 }
1387
1388
1389 static inline int
1390 iscompressed(const char *name)
1391 {
1392   int l = strlen(name);
1393   return l > 3 && !strcmp(name + l - 3, ".gz") ? 1 : 0;
1394 }
1395
1396
1397 /* load callback */
1398
1399 int
1400 load_stub(Pool *pool, Repodata *data, void *dp)
1401 {
1402   const char *filename, *descrdir, *repomdtype;
1403   const unsigned char *filechksum;
1404   Id filechksumtype;
1405   struct repoinfo *cinfo;
1406   FILE *fp;
1407   Id defvendor;
1408   char ext[3];
1409
1410   cinfo = data->repo->appdata;
1411
1412   filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
1413   if (filename)
1414     {
1415       /* susetags load */
1416       ext[0] = filename[9];
1417       ext[1] = filename[10];
1418       ext[2] = 0;
1419 #if 1
1420       printf("[%s:%s", data->repo->name, ext);
1421 #endif
1422       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1423         {
1424           printf(" cached]\n"); fflush(stdout);
1425           return 1;
1426         }
1427 #if 1
1428       printf(" fetching]\n"); fflush(stdout);
1429 #endif
1430       defvendor = repo_lookup_id(data->repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1431       descrdir = repo_lookup_str(data->repo, SOLVID_META, SUSETAGS_DESCRDIR);
1432       if (!descrdir)
1433         descrdir = "suse/setup/descr";
1434       filechksumtype = 0;
1435       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
1436       if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1437         return 0;
1438       repo_add_susetags(data->repo, fp, defvendor, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1439       fclose(fp);
1440       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1441       return 1;
1442     }
1443
1444   repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
1445   if (repomdtype)
1446     {
1447       if (!strcmp(repomdtype, "filelists"))
1448         strcpy(ext, "FL");
1449       else if (!strcmp(repomdtype, "deltainfo"))
1450         strcpy(ext, "DL");
1451       else
1452         return 0;
1453 #if 1
1454       printf("[%s:%s", data->repo->name, ext);
1455 #endif
1456       if (usecachedrepo(data->repo, ext, cinfo->extcookie, 0))
1457         {
1458           printf(" cached]\n");fflush(stdout);
1459           return 1;
1460         }
1461       printf(" fetching]\n"); fflush(stdout);
1462       filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
1463       filechksumtype = 0;
1464       filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
1465       if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
1466         return 0;
1467       if (!strcmp(ext, "FL"))
1468         repo_add_rpmmd(data->repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES);
1469       else if (!strcmp(ext, "DL"))
1470         repo_add_deltainfoxml(data->repo, fp, REPO_USE_LOADING);
1471       fclose(fp);
1472       writecachedrepo(data->repo, data, ext, cinfo->extcookie);
1473       return 1;
1474     }
1475
1476   return 0;
1477 }
1478
1479 static unsigned char installedcookie[32];
1480
1481 #ifdef DEBIAN
1482 void
1483 repo_add_debdb(Repo *repo, int flags)
1484 {
1485   FILE *fp;
1486   if ((fp = fopen("/var/lib/dpkg/status", "r")) == 0)
1487     {
1488       perror("/var/lib/dpkg/status");
1489       exit(1);
1490     }
1491   repo_add_debpackages(repo, fp, flags);
1492   fclose(fp);
1493 }
1494
1495 static int
1496 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1497 {
1498   int i;
1499   for (i = 0; i < buflen; i++) 
1500     {    
1501 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
1502                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
1503                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
1504                 : -1)
1505       int v = c2h(*str);
1506       str++;
1507       if (v < 0) 
1508         return 0;
1509       buf[i] = v; 
1510       v = c2h(*str);
1511       str++;
1512       if (v < 0) 
1513         return 0;
1514       buf[i] = (buf[i] << 4) | v; 
1515 #undef c2h
1516     }    
1517   return buflen;
1518 }
1519
1520 const char *
1521 debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
1522 {
1523   char buf[4096];
1524   Id chksumtype;
1525   unsigned char *chksum;
1526   Id curchksumtype;
1527   int l, compl;
1528   char *ch, *fn, *bp;
1529   char *filename;
1530   static char *basearch;
1531   char *binarydir;
1532   int lbinarydir;
1533
1534   if (!basearch)
1535     {
1536       struct utsname un;
1537       if (uname(&un))
1538         {
1539           perror("uname");
1540           exit(1);
1541         }
1542       basearch = strdup(un.machine);
1543       if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
1544         basearch[1] = '3';
1545     }
1546   binarydir = sat_dupjoin("binary-", basearch, "/");
1547   lbinarydir = strlen(binarydir);
1548   compl = strlen(comp);
1549   rewind(fp);
1550   curchksumtype = 0;
1551   filename = 0;
1552   chksum = sat_malloc(32);
1553   chksumtype = 0;
1554   while(fgets(buf, sizeof(buf), fp))
1555     {
1556       l = strlen(buf);
1557       if (l == 0)
1558         continue;
1559       while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
1560         buf[--l] = 0;
1561       if (!strncasecmp(buf, "MD5Sum:", 7))
1562         {
1563           curchksumtype = REPOKEY_TYPE_MD5;
1564           continue;
1565         }
1566       if (!strncasecmp(buf, "SHA1:", 5))
1567         {
1568           curchksumtype = REPOKEY_TYPE_SHA1;
1569           continue;
1570         }
1571       if (!strncasecmp(buf, "SHA256:", 7))
1572         {
1573           curchksumtype = REPOKEY_TYPE_SHA256;
1574           continue;
1575         }
1576       if (!curchksumtype)
1577         continue;
1578       bp = buf;
1579       if (*bp++ != ' ')
1580         {
1581           curchksumtype = 0;
1582           continue;
1583         }
1584       ch = bp;
1585       while (*bp && *bp != ' ' && *bp != '\t')
1586         bp++;
1587       if (!*bp)
1588         continue;
1589       *bp++ = 0;
1590       while (*bp == ' ' || *bp == '\t')
1591         bp++;
1592       while (*bp && *bp != ' ' && *bp != '\t')
1593         bp++;
1594       if (!*bp)
1595         continue;
1596       while (*bp == ' ' || *bp == '\t')
1597         bp++;
1598       fn = bp;
1599       if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
1600         continue;
1601       bp += compl + 1;
1602       if (strncmp(bp, binarydir, lbinarydir))
1603         continue;
1604       bp += lbinarydir;
1605       if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
1606         {
1607           if (filename && !strcmp(bp, "Packages"))
1608             continue;
1609           if (chksumtype && sat_chksum_len(chksumtype) > sat_chksum_len(curchksumtype))
1610             continue;
1611           if (!hexstr2bytes(chksum, ch, sat_chksum_len(curchksumtype)))
1612             continue;
1613           sat_free(filename);
1614           filename = strdup(fn);
1615           chksumtype = curchksumtype;
1616         }
1617     }
1618   free(binarydir);
1619   if (filename)
1620     {
1621       fn = sat_dupjoin("/", filename, 0);
1622       sat_free(filename);
1623       filename = sat_dupjoin("dists/", cinfo->name, fn);
1624       sat_free(fn);
1625     }
1626   if (!chksumtype)
1627     chksum = sat_free(chksum);
1628   *chksump = chksum;
1629   *chksumtypep = chksumtype;
1630   return filename;
1631 }
1632 #endif
1633
1634 void
1635 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
1636 {
1637   Repo *repo;
1638   struct repoinfo *cinfo;
1639   int i;
1640   FILE *fp;
1641   FILE *sigfp;
1642   const char *filename;
1643   const unsigned char *filechksum;
1644   Id filechksumtype;
1645   const char *descrdir;
1646   int defvendor;
1647   struct stat stb;
1648   Pool *sigpool = 0;
1649   Repodata *data;
1650   int badchecksum;
1651   int dorefresh;
1652 #ifdef DEBIAN
1653   FILE *fpr;
1654   int j;
1655 #endif
1656
1657   repo = repo_create(pool, "@System");
1658 #ifndef DEBIAN
1659   printf("rpm database:");
1660   if (stat("/var/lib/rpm/Packages", &stb))
1661     memset(&stb, 0, sizeof(&stb));
1662 #else
1663   printf("dpgk database:");
1664   if (stat("/var/lib/dpkg/status", &stb))
1665     memset(&stb, 0, sizeof(&stb));
1666 #endif
1667   calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, installedcookie);
1668   if (usecachedrepo(repo, 0, installedcookie, 0))
1669     printf(" cached\n");
1670   else
1671     {
1672 #ifndef DEBIAN
1673       FILE *ofp;
1674       int done = 0;
1675 #endif
1676       printf(" reading\n");
1677
1678 #ifdef PRODUCTS_PATH
1679       repo_add_products(repo, PRODUCTS_PATH, 0, REPO_NO_INTERNALIZE);
1680 #endif
1681 #ifndef DEBIAN
1682       if ((ofp = fopen(calccachepath(repo, 0), "r")) != 0)
1683         {
1684           Repo *ref = repo_create(pool, "@System.old");
1685           if (!repo_add_solv(ref, ofp))
1686             {
1687               repo_add_rpmdb(repo, ref, 0, REPO_REUSE_REPODATA);
1688               done = 1;
1689             }
1690           fclose(ofp);
1691           repo_free(ref, 1);
1692         }
1693       if (!done)
1694         repo_add_rpmdb(repo, 0, 0, REPO_REUSE_REPODATA);
1695 #else
1696         repo_add_debdb(repo, REPO_REUSE_REPODATA);
1697 #endif
1698       writecachedrepo(repo, 0, 0, installedcookie);
1699     }
1700   pool_set_installed(pool, repo);
1701
1702   for (i = 0; i < nrepoinfos; i++)
1703     {
1704       cinfo = repoinfos + i;
1705       if (!cinfo->enabled)
1706         continue;
1707
1708       repo = repo_create(pool, cinfo->alias);
1709       cinfo->repo = repo;
1710       repo->appdata = cinfo;
1711       repo->priority = 99 - cinfo->priority;
1712
1713       dorefresh = cinfo->autorefresh;
1714       if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0), &stb) == 0)
1715         {
1716           if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
1717             dorefresh = 0;
1718         }
1719       if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
1720         {
1721           printf("repo '%s':", cinfo->alias);
1722           printf(" cached\n");
1723           continue;
1724         }
1725       badchecksum = 0;
1726       switch (cinfo->type)
1727         {
1728         case TYPE_RPMMD:
1729           printf("rpmmd repo '%s':", cinfo->alias);
1730           fflush(stdout);
1731           if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
1732             {
1733               printf(" no repomd.xml file, skipped\n");
1734               repo_free(repo, 1);
1735               cinfo->repo = 0;
1736               break;
1737             }
1738           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1739           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1740             {
1741               printf(" cached\n");
1742               fclose(fp);
1743               break;
1744             }
1745           if (cinfo->repo_gpgcheck)
1746             {
1747               sigfp = curlfopen(cinfo, "repodata/repomd.xml.asc", 0, 0, 0, 0);
1748               if (!sigfp)
1749                 {
1750                   printf(" unsigned, skipped\n");
1751                   fclose(fp);
1752                   break;
1753                 }
1754               if (!sigpool)
1755                 sigpool = read_sigs();
1756               if (!checksig(sigpool, fp, sigfp))
1757                 {
1758                   printf(" checksig failed, skipped\n");
1759                   fclose(sigfp);
1760                   fclose(fp);
1761                   break;
1762                 }
1763               fclose(sigfp);
1764             }
1765           repo_add_repomdxml(repo, fp, 0);
1766           fclose(fp);
1767           printf(" fetching\n");
1768           filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
1769           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1770             {
1771               repo_add_rpmmd(repo, fp, 0, 0);
1772               fclose(fp);
1773             }
1774           if (badchecksum)
1775             break;      /* hopeless */
1776
1777           filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
1778           if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1779             {
1780               repo_add_updateinfoxml(repo, fp, 0);
1781               fclose(fp);
1782             }
1783
1784           data = repo_add_repodata(repo, 0);
1785           if (!repomd_add_ext(repo, data, "deltainfo"))
1786             repomd_add_ext(repo, data, "prestodelta");
1787           repomd_add_ext(repo, data, "filelists");
1788           repodata_internalize(data);
1789           if (!badchecksum)
1790             writecachedrepo(repo, 0, 0, cinfo->cookie);
1791           repodata_create_stubs(repo_last_repodata(repo));
1792           break;
1793
1794         case TYPE_SUSETAGS:
1795           printf("susetags repo '%s':", cinfo->alias);
1796           fflush(stdout);
1797           descrdir = 0;
1798           defvendor = 0;
1799           if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
1800             {
1801               printf(" no content file, skipped\n");
1802               repo_free(repo, 1);
1803               cinfo->repo = 0;
1804               break;
1805             }
1806           calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
1807           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1808             {
1809               printf(" cached\n");
1810               fclose(fp);
1811               break;
1812             }
1813           if (cinfo->repo_gpgcheck)
1814             {
1815               sigfp = curlfopen(cinfo, "content.asc", 0, 0, 0, 0);
1816               if (!sigfp)
1817                 {
1818                   printf(" unsigned, skipped\n");
1819                   fclose(fp);
1820                   break;
1821                 }
1822               if (sigfp)
1823                 {
1824                   if (!sigpool)
1825                     sigpool = read_sigs();
1826                   if (!checksig(sigpool, fp, sigfp))
1827                     {
1828                       printf(" checksig failed, skipped\n");
1829                       fclose(sigfp);
1830                       fclose(fp);
1831                       break;
1832                     }
1833                   fclose(sigfp);
1834                 }
1835             }
1836           repo_add_content(repo, fp, 0);
1837           fclose(fp);
1838           defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
1839           descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
1840           if (!descrdir)
1841             descrdir = "suse/setup/descr";
1842           filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
1843           if (!filename)
1844             filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
1845           if (!filename)
1846             {
1847               printf(" no packages file entry, skipped\n");
1848               break;
1849             }
1850           printf(" fetching\n");
1851           if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) == 0)
1852             break;      /* hopeless */
1853           repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES);
1854           fclose(fp);
1855           /* add default language */
1856           filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
1857           if (!filename)
1858             filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
1859           if (filename)
1860             {
1861               if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1862                 {
1863                   repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES);
1864                   fclose(fp);
1865                 }
1866             }
1867           repo_internalize(repo);
1868           data = repo_add_repodata(repo, 0);
1869           susetags_add_ext(repo, data);
1870           repodata_internalize(data);
1871           if (!badchecksum)
1872             writecachedrepo(repo, 0, 0, cinfo->cookie);
1873           repodata_create_stubs(repo_last_repodata(repo));
1874           break;
1875
1876 #ifdef DEBIAN
1877         case TYPE_DEBIAN:
1878           printf("debian repo '%s':", cinfo->alias);
1879           fflush(stdout);
1880           filename = sat_dupjoin("dists/", cinfo->name, "/Release");
1881           if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
1882             {
1883               printf(" no Release file, skipped\n");
1884               repo_free(repo, 1);
1885               cinfo->repo = 0;
1886               free((char *)filename);
1887               break;
1888             }
1889           sat_free((char *)filename);
1890           if (cinfo->repo_gpgcheck)
1891             {
1892               filename = sat_dupjoin("dists/", cinfo->name, "/Release.gpg");
1893               sigfp = curlfopen(cinfo, filename, 0, 0, 0, 0);
1894               sat_free((char *)filename);
1895               if (!sigfp)
1896                 {
1897                   printf(" unsigned, skipped\n");
1898                   fclose(fpr);
1899                   break;
1900                 }
1901               if (!sigpool)
1902                 sigpool = read_sigs();
1903               if (!checksig(sigpool, fpr, sigfp))
1904                 {
1905                   printf(" checksig failed, skipped\n");
1906                   fclose(sigfp);
1907                   fclose(fpr);
1908                   break;
1909                 }
1910               fclose(sigfp);
1911             }
1912           calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
1913           if (usecachedrepo(repo, 0, cinfo->cookie, 1))
1914             {
1915               printf(" cached\n");
1916               fclose(fpr);
1917               break;
1918             }
1919           printf(" fetching\n");
1920           for (j = 0; j < cinfo->ncomponents; j++)
1921             {
1922               if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
1923                 {
1924                   printf("[component %s not found]\n", cinfo->components[j]);
1925                   continue;
1926                 }
1927               if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, &badchecksum)) != 0)
1928                 {
1929                   repo_add_debpackages(repo, fp, 0);
1930                   fclose(fp);
1931                 }
1932               sat_free((char *)filechksum);
1933               sat_free((char *)filename);
1934             }
1935           fclose(fpr);
1936           if (!badchecksum)
1937             writecachedrepo(repo, 0, 0, cinfo->cookie);
1938           break;
1939 #endif
1940
1941         default:
1942           printf("unsupported repo '%s': skipped\n", cinfo->alias);
1943           repo_free(repo, 1);
1944           cinfo->repo = 0;
1945           break;
1946         }
1947     }
1948   if (sigpool)
1949     pool_free(sigpool);
1950 }
1951
1952
1953 int
1954 str2archid(Pool *pool, char *arch)
1955 {
1956   Id id;
1957   if (!*arch)
1958     return 0;
1959   id = str2id(pool, arch, 0);
1960   if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
1961     return id;
1962   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
1963     return 0;
1964   return id;
1965 }
1966
1967
1968 #define DEPGLOB_NAME     1
1969 #define DEPGLOB_DEP      2
1970 #define DEPGLOB_NAMEDEP  3
1971
1972 int
1973 depglob(Pool *pool, char *name, Queue *job, int what)
1974 {
1975   Id p, pp;
1976   Id id = str2id(pool, name, 0);
1977   int i, match = 0;
1978
1979   if (id)
1980     {
1981       FOR_PROVIDES(p, pp, id)
1982         {
1983           Solvable *s = pool->solvables + p;
1984           match = 1;
1985           if (s->name == id && (what & DEPGLOB_NAME) != 0)
1986             {
1987               queue_push2(job, SOLVER_SOLVABLE_NAME, id);
1988               return 1;
1989             }
1990         }
1991       if (match)
1992         {
1993           if (what == DEPGLOB_NAMEDEP)
1994             printf("[using capability match for '%s']\n", name);
1995           queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
1996           return 1;
1997         }
1998     }
1999
2000   if (strpbrk(name, "[*?") == 0)
2001     return 0;
2002
2003   if ((what & DEPGLOB_NAME) != 0)
2004     {
2005       /* looks like a name glob. hard work. */
2006       for (p = 1; p < pool->nsolvables; p++)
2007         {
2008           Solvable *s = pool->solvables + p;
2009           if (!s->repo || !pool_installable(pool, s))
2010             continue;
2011           id = s->name;
2012           if (fnmatch(name, id2str(pool, id), 0) == 0)
2013             {
2014               for (i = 0; i < job->count; i += 2)
2015                 if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id)
2016                   break;
2017               if (i == job->count)
2018                 queue_push2(job, SOLVER_SOLVABLE_NAME, id);
2019               match = 1;
2020             }
2021         }
2022       if (match)
2023         return 1;
2024     }
2025   if ((what & DEPGLOB_DEP))
2026     {
2027       /* looks like a dep glob. really hard work. */
2028       for (id = 1; id < pool->ss.nstrings; id++)
2029         {
2030           if (!pool->whatprovides[id])
2031             continue;
2032           if (fnmatch(name, id2str(pool, id), 0) == 0)
2033             {
2034               if (!match && what == DEPGLOB_NAMEDEP)
2035                 printf("[using capability match for '%s']\n", name);
2036               for (i = 0; i < job->count; i += 2)
2037                 if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id)
2038                   break;
2039               if (i == job->count)
2040                 queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id);
2041               match = 1;
2042             }
2043         }
2044       if (match)
2045         return 1;
2046     }
2047   return 0;
2048 }
2049
2050 int
2051 limitrelation(Pool *pool, Queue *job, int flags, Id evr)
2052 {
2053   int i, j;
2054   Id p, pp;
2055   for (i = j = 0; i < job->count; i += 2)
2056     {
2057       Id select = job->elements[i] & SOLVER_SELECTMASK;
2058       if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
2059         {
2060           fprintf(stderr, "limitrelation only works on name/provides jobs\n");
2061           exit(1);
2062         }
2063       job->elements[i + 1] = rel2id(pool, job->elements[i + 1], evr, flags, 1);
2064       if (flags == REL_ARCH)
2065         job->elements[i] |= SOLVER_SETARCH;
2066       if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && job->elements[i])
2067         {
2068 #ifndef DEBIAN
2069           const char *evrstr = id2str(pool, evr);
2070           if (!strchr(evrstr, '-'))
2071             job->elements[i] |= SOLVER_SETEV;
2072           else
2073 #endif
2074             job->elements[i] |= SOLVER_SETEVR;
2075         }
2076       /* make sure we still have matches */
2077       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2078         break;
2079       if (p)
2080         {
2081           job->elements[j] = job->elements[i];
2082           job->elements[j + 1] = job->elements[i + 1];
2083           j += 2;
2084         }
2085     }
2086   queue_truncate(job, j);
2087   return j / 2;
2088 }
2089
2090 int
2091 limitrelation_arch(Pool *pool, Queue *job, int flags, char *evr)
2092 {
2093   char *r;
2094   Id archid;
2095   if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2096     {
2097       *r = 0;
2098       limitrelation(pool, job, REL_ARCH, archid);
2099       limitrelation(pool, job, flags, str2id(pool, evr, 1));
2100       *r = '.';
2101     }
2102   else
2103     limitrelation(pool, job, flags, str2id(pool, evr, 1));
2104   return job->count / 2;
2105 }
2106
2107 int
2108 limitrepo(Pool *pool, Id repofilter, Queue *job)
2109 {
2110   Queue mq;
2111   Id p, pp;
2112   int i, j;
2113   Solvable *s;
2114
2115   queue_init(&mq);
2116   for (i = j = 0; i < job->count; i += 2)
2117     {
2118       queue_empty(&mq);
2119       FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1])
2120         {
2121           s = pool_id2solvable(pool, p);
2122           if (s->repo && s->repo->repoid == repofilter)
2123              queue_push(&mq, p);
2124         }
2125       if (mq.count)
2126         {
2127           /* here we assume that repo == vendor, so we also set SOLVER_SETVENDOR */
2128           if (mq.count == 1)
2129             {
2130               job->elements[j] = SOLVER_SOLVABLE | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_NOAUTOSET;
2131               job->elements[j + 1] = mq.elements[0];
2132             }
2133           else
2134             {
2135               job->elements[j] = SOLVER_SOLVABLE_ONE_OF | (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO;
2136               job->elements[j + 1] = pool_queuetowhatprovides(pool, &mq);
2137             }
2138           j += 2;
2139         }
2140     }
2141   queue_truncate(job, j);
2142   queue_free(&mq);
2143   return j / 2;
2144 }
2145
2146 int
2147 mkselect(Pool *pool, int mode, char *name, Queue *job)
2148 {
2149   char *r, *r2;
2150   Id archid;
2151
2152   if (*name == '/')
2153     {
2154       Dataiterator di;
2155       int type = strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
2156       Queue q;
2157       queue_init(&q);
2158       dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
2159       while (dataiterator_step(&di))
2160         {
2161           Solvable *s = pool->solvables + di.solvid;
2162           if (!s->repo || !pool_installable(pool, s))
2163             continue;
2164           queue_push(&q, di.solvid);
2165           dataiterator_skip_solvable(&di);
2166         }
2167       dataiterator_free(&di);
2168       if (q.count)
2169         {
2170           printf("[using file list match for '%s']\n", name);
2171           if (q.count > 1)
2172             queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
2173           else
2174             queue_push2(job, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
2175           queue_free(&q);
2176           return job->count / 2;
2177         }
2178     }
2179   if ((r = strpbrk(name, "<=>")) != 0)
2180     {
2181       /* relation case, support:
2182        * depglob rel
2183        * depglob.arch rel
2184        */
2185       int rflags = 0;
2186       int nend = r - name;
2187       char oldnend;
2188       for (; *r; r++)
2189         {
2190           if (*r == '<')
2191             rflags |= REL_LT;
2192           else if (*r == '=')
2193             rflags |= REL_EQ;
2194           else if (*r == '>')
2195             rflags |= REL_GT;
2196           else
2197             break;
2198         }
2199       while (*r && *r == ' ' && *r == '\t')
2200         r++;
2201       while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t'))
2202         nend--;
2203       if (!*name || !*r)
2204         {
2205           fprintf(stderr, "bad relation\n");
2206           exit(1);
2207         }
2208       oldnend = name[nend];
2209       name[nend] = 0;
2210       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2211         {
2212           name[nend] = oldnend;
2213           limitrelation(pool, job, rflags, str2id(pool, r, 1));
2214           return job->count / 2;
2215         }
2216       if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
2217         {
2218           *r2 = 0;
2219           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2220             {
2221               name[nend] = oldnend;
2222               *r2 = '.';
2223               limitrelation(pool, job, REL_ARCH, archid);
2224               limitrelation(pool, job, rflags, str2id(pool, r, 1));
2225               return job->count / 2;
2226             }
2227           *r2 = '.';
2228         }
2229       name[nend] = oldnend;
2230     }
2231   else
2232     {
2233       /* no relation case, support:
2234        * depglob
2235        * depglob.arch
2236        * nameglob-version
2237        * nameglob-version.arch
2238        * nameglob-version-release
2239        * nameglob-version-release.arch
2240        */
2241       if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2242         return job->count / 2;
2243       if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
2244         {
2245           *r = 0;
2246           if (depglob(pool, name, job, DEPGLOB_NAMEDEP))
2247             {
2248               *r = '.';
2249               limitrelation(pool, job, REL_ARCH, archid);
2250               return job->count / 2;
2251             }
2252           *r = '.';
2253         }
2254       if ((r = strrchr(name, '-')) != 0)
2255         {
2256           *r = 0;
2257           if (depglob(pool, name, job, DEPGLOB_NAME))
2258             {
2259               /* have just the version */
2260               limitrelation_arch(pool, job, REL_EQ, r + 1);
2261               *r = '-';
2262               return job->count / 2;
2263             }
2264           if ((r2 = strrchr(name, '-')) != 0)
2265             {
2266               *r = '-';
2267               *r2 = 0;
2268               r = r2;
2269               if (depglob(pool, name, job, DEPGLOB_NAME))
2270                 {
2271                   /* have version-release */
2272                   limitrelation_arch(pool, job, REL_EQ, r + 1);
2273                   *r = '-';
2274                   return job->count / 2;
2275                 }
2276             }
2277           *r = '-';
2278         }
2279     }
2280   return 0;
2281 }
2282
2283
2284 int
2285 yesno(const char *str)
2286 {
2287   char inbuf[128], *ip;
2288
2289   for (;;)
2290     {
2291       printf("%s", str);
2292       fflush(stdout);
2293       *inbuf = 0;
2294       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
2295         {
2296           printf("Abort.\n");
2297           exit(1);
2298         }
2299       while (*ip == ' ' || *ip == '\t')
2300         ip++;
2301       if (*ip == 'q')
2302         {
2303           printf("Abort.\n");
2304           exit(1);
2305         }
2306       if (*ip == 'y' || *ip == 'n')
2307         return *ip == 'y' ? 1 : 0;
2308     }
2309 }
2310
2311 #ifndef DEBIAN
2312
2313 struct fcstate {
2314   FILE **newpkgsfps;
2315   Queue *checkq;
2316   int newpkgscnt;
2317   void *rpmdbstate;
2318 };
2319
2320 static void *
2321 fileconflict_cb(Pool *pool, Id p, void *cbdata)
2322 {
2323   struct fcstate *fcstate = cbdata;
2324   Solvable *s;
2325   Id rpmdbid;
2326   int i;
2327   FILE *fp;
2328
2329   if (!p)
2330     {
2331       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
2332       return 0;
2333     }
2334   s = pool_id2solvable(pool, p);
2335   if (pool->installed && s->repo == pool->installed)
2336     {
2337       if (!s->repo->rpmdbid)
2338         return 0;
2339       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
2340       if (!rpmdbid)
2341         return 0;
2342        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
2343     }
2344   for (i = 0; i < fcstate->newpkgscnt; i++)
2345     if (fcstate->checkq->elements[i] == p)
2346       break;
2347   if (i == fcstate->newpkgscnt)
2348     return 0;
2349   fp = fcstate->newpkgsfps[i];
2350   if (!fp)
2351     return 0;
2352   rewind(fp);
2353   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
2354 }
2355
2356
2357 void
2358 runrpm(const char *arg, const char *name, int dupfd3)
2359 {
2360   pid_t pid;
2361   int status;
2362
2363   if ((pid = fork()) == (pid_t)-1)
2364     {
2365       perror("fork");
2366       exit(1);
2367     }
2368   if (pid == 0)
2369     {
2370       if (dupfd3 != -1 && dupfd3 != 3)
2371         {
2372           dup2(dupfd3, 3);
2373           close(dupfd3);
2374         }
2375       if (dupfd3 != -1)
2376         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2377       if (strcmp(arg, "-e") == 0)
2378         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2379       else
2380         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
2381       perror("rpm");
2382       _exit(0);
2383     }
2384   while (waitpid(pid, &status, 0) != pid)
2385     ;
2386   if (status)
2387     {
2388       printf("rpm failed\n");
2389       exit(1);
2390     }
2391 }
2392
2393 #endif
2394
2395 #ifdef DEBIAN
2396
2397 void
2398 rundpkg(const char *arg, const char *name, int dupfd3)
2399 {
2400   pid_t pid;
2401   int status;
2402
2403   if ((pid = fork()) == (pid_t)-1)
2404     {
2405       perror("fork");
2406       exit(1);
2407     }
2408   if (pid == 0)
2409     {
2410       if (dupfd3 != -1 && dupfd3 != 3)
2411         {
2412           dup2(dupfd3, 3);
2413           close(dupfd3);
2414         }
2415       if (dupfd3 != -1)
2416         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
2417       if (strcmp(arg, "--install") == 0)
2418         execlp("dpkg", "dpkg", "--install", "--force", "all", name, (char *)0);
2419       else
2420         execlp("dpkg", "dpkg", "--remove", "--force", "all", name, (char *)0);
2421       perror("dpkg");
2422       _exit(0);
2423     }
2424   while (waitpid(pid, &status, 0) != pid)
2425     ;
2426   if (status)
2427     {
2428       printf("dpkg failed\n");
2429       exit(1);
2430     }
2431 }
2432
2433 #endif
2434
2435 static Id
2436 nscallback(Pool *pool, void *data, Id name, Id evr)
2437 {
2438   if (name == NAMESPACE_PRODUCTBUDDY)
2439     {    
2440       /* SUSE specific hack: each product has an associated rpm */
2441       Solvable *s = pool->solvables + evr; 
2442       Id p, pp, cap; 
2443       
2444       cap = str2id(pool, pool_tmpjoin(pool, "product(", id2str(pool, s->name) + 8, ")"), 0);
2445       if (!cap)
2446         return 0;
2447       cap = rel2id(pool, cap, s->evr, REL_EQ, 0);
2448       if (!cap)
2449         return 0;
2450       FOR_PROVIDES(p, pp, cap) 
2451         {
2452           Solvable *ps = pool->solvables + p; 
2453           if (ps->repo == s->repo && ps->arch == s->arch)
2454             break;
2455         }
2456       return p;
2457     }
2458   return 0;
2459 }
2460
2461 #ifdef SOFTLOCKS_PATH
2462
2463 void
2464 addsoftlocks(Pool *pool, Queue *job)
2465 {
2466   FILE *fp;
2467   Id type, id, p, pp;
2468   char *bp, *ep, buf[4096];
2469
2470   if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
2471     return;
2472   while((bp = fgets(buf, sizeof(buf), fp)) != 0)
2473     {
2474       while (*bp == ' ' || *bp == '\t')
2475         bp++;
2476       if (!*bp || *bp == '#')
2477         continue;
2478       for (ep = bp; *ep; ep++)
2479         if (*ep == ' ' || *ep == '\t' || *ep == '\n')
2480           break;
2481       *ep = 0;
2482       type = SOLVER_SOLVABLE_NAME;
2483       if (!strncmp(bp, "provides:", 9) && bp[9])
2484         {
2485           type = SOLVER_SOLVABLE_PROVIDES;
2486           bp += 9;
2487         }
2488       id = str2id(pool, bp, 1);
2489       if (pool->installed)
2490         {
2491           FOR_JOB_SELECT(p, pp, type, id)
2492             if (pool->solvables[p].repo == pool->installed)
2493               break;
2494           if (p)
2495             continue;   /* ignore, as it is already installed */
2496         }
2497       queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
2498     }
2499   fclose(fp);
2500 }
2501
2502 #endif
2503
2504
2505 void
2506 rewrite_repos(Pool *pool, Id *addedfileprovides)
2507 {
2508   Repo *repo;
2509   Repodata *data;
2510   Map providedids;
2511   Queue fileprovidesq;
2512   Id id;
2513   int i, j, n, nprovidedids;
2514   struct repoinfo *cinfo;
2515
2516   map_init(&providedids, pool->ss.nstrings);
2517   queue_init(&fileprovidesq);
2518   for (nprovidedids = 0; (id = addedfileprovides[nprovidedids]) != 0; nprovidedids++)
2519     MAPSET(&providedids, id);
2520   FOR_REPOS(i, repo)
2521     {
2522       /* make sure this repo has just one main repodata */
2523       if (!repo->nrepodata)
2524         continue;
2525       cinfo = repo->appdata;
2526       data = repo->repodata + 0;
2527       if (data->store.pagefd == -1)
2528         continue;
2529       if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
2530         {
2531           n = 0;
2532           for (j = 0; j < fileprovidesq.count; j++)
2533             if (MAPTST(&providedids, fileprovidesq.elements[j]))
2534               n++;
2535           if (n == nprovidedids)
2536             continue;   /* nothing new added */
2537         }
2538       /* oh my! */
2539       for (j = 0; addedfileprovides[j]; j++)
2540         repodata_add_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, addedfileprovides[j]);
2541       repodata_internalize(data);
2542       writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
2543     }
2544   queue_free(&fileprovidesq);
2545   map_free(&providedids);
2546 }
2547
2548 #define MODE_LIST        0
2549 #define MODE_INSTALL     1
2550 #define MODE_ERASE       2
2551 #define MODE_UPDATE      3
2552 #define MODE_DISTUPGRADE 4
2553 #define MODE_VERIFY      5
2554 #define MODE_PATCH       6
2555 #define MODE_INFO        7
2556 #define MODE_REPOLIST    8
2557 #define MODE_SEARCH      9
2558 #define MODE_ERASECLEAN  10
2559
2560 void
2561 usage(int r)
2562 {
2563   fprintf(stderr, "Usage: solv COMMAND <select>\n");
2564   fprintf(stderr, "\n");
2565   fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
2566   fprintf(stderr, "                  versions from the repositories\n");
2567   fprintf(stderr, "    erase:        erase installed packages\n");
2568   fprintf(stderr, "    info:         display package information\n");
2569   fprintf(stderr, "    install:      install packages\n");
2570   fprintf(stderr, "    list:         list packages\n");
2571   fprintf(stderr, "    repos:        list enabled repositories\n");
2572   fprintf(stderr, "    search:       search name/summary/description\n");
2573   fprintf(stderr, "    update:       update installed packages\n");
2574   fprintf(stderr, "    verify:       check dependencies of installed packages\n");
2575   fprintf(stderr, "\n");
2576   exit(r);
2577 }
2578
2579 int
2580 main(int argc, char **argv)
2581 {
2582   Pool *pool;
2583   Repo *commandlinerepo = 0;
2584   Id *commandlinepkgs = 0;
2585   Id p, pp;
2586   struct repoinfo *repoinfos;
2587   int nrepoinfos = 0;
2588   int mainmode = 0, mode = 0;
2589   int i, newpkgs;
2590   Queue job, checkq;
2591   Solver *solv = 0;
2592   Transaction *trans;
2593   char inbuf[128], *ip;
2594   int allpkgs = 0;
2595   FILE **newpkgsfps;
2596   Id *addedfileprovides = 0;
2597   Id repofilter = 0;
2598
2599   argc--;
2600   argv++;
2601   if (!argv[0])
2602     usage(1);
2603   if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
2604     {
2605       mainmode = MODE_INSTALL;
2606       mode = SOLVER_INSTALL;
2607     }
2608   else if (!strcmp(argv[0], "patch"))
2609     {
2610       mainmode = MODE_PATCH;
2611       mode = SOLVER_INSTALL;
2612     }
2613   else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
2614     {
2615       mainmode = MODE_ERASE;
2616       mode = SOLVER_ERASE;
2617     }
2618   else if (!strcmp(argv[0], "eraseclean") || !strcmp(argv[0], "rmclean"))
2619     {
2620       mainmode = MODE_ERASECLEAN;
2621       mode = SOLVER_ERASE;
2622     }
2623   else if (!strcmp(argv[0], "list"))
2624     {
2625       mainmode = MODE_LIST;
2626       mode = 0;
2627     }
2628   else if (!strcmp(argv[0], "info"))
2629     {
2630       mainmode = MODE_INFO;
2631       mode = 0;
2632     }
2633   else if (!strcmp(argv[0], "search"))
2634     {
2635       mainmode = MODE_SEARCH;
2636       mode = 0;
2637     }
2638   else if (!strcmp(argv[0], "verify"))
2639     {
2640       mainmode = MODE_VERIFY;
2641       mode = SOLVER_VERIFY;
2642     }
2643   else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
2644     {
2645       mainmode = MODE_UPDATE;
2646       mode = SOLVER_UPDATE;
2647     }
2648   else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
2649     {
2650       mainmode = MODE_DISTUPGRADE;
2651       mode = SOLVER_UPDATE;
2652     }
2653   else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
2654     {
2655       mainmode = MODE_REPOLIST;
2656       mode = 0;
2657     }
2658   else
2659     usage(1);
2660
2661   pool = pool_create();
2662
2663 #if 0
2664   {
2665     const char *langs[] = {"de_DE", "de", "en"};
2666     pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
2667   }
2668 #endif
2669
2670 #ifdef FEDORA
2671   pool->obsoleteusescolors = 1;
2672 #endif
2673   pool_setloadcallback(pool, load_stub, 0);
2674   pool->nscallback = nscallback;
2675   // pool_setdebuglevel(pool, 2);
2676   setarch(pool);
2677   repoinfos = read_repoinfos(pool, REPOINFO_PATH, &nrepoinfos);
2678
2679   if (mainmode == MODE_REPOLIST)
2680     {
2681       int j = 1;
2682       for (i = 0; i < nrepoinfos; i++)
2683         {
2684           struct repoinfo *cinfo = repoinfos + i;
2685           if (!cinfo->enabled)
2686             continue;
2687           printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
2688         }
2689       exit(0);
2690     }
2691
2692   read_repos(pool, repoinfos, nrepoinfos);
2693
2694   if (argc > 2 && !strcmp(argv[1], "-r"))
2695     {
2696       const char *rname = argv[2], *rp;
2697       for (rp = rname; *rp; rp++)
2698         if (*rp <= '0' || *rp >= '9')
2699           break;
2700       if (!*rp)
2701         {
2702           /* repo specified by number */
2703           int rnum = atoi(rname);
2704           for (i = 0; i < nrepoinfos; i++)
2705             {
2706               struct repoinfo *cinfo = repoinfos + i;
2707               if (!cinfo->enabled)
2708                 continue;
2709               if (--rnum == 0)
2710                 repofilter = cinfo->repo->repoid;
2711             }
2712         }
2713       else
2714         {
2715           /* repo specified by alias */
2716           Repo *repo;
2717           FOR_REPOS(i, repo)
2718             {
2719               if (!strcasecmp(rname, repo->name))
2720                 repofilter = repo->repoid;
2721             }
2722         }
2723       if (!repofilter)
2724         {
2725           fprintf(stderr, "%s: no such repo\n", rname);
2726           exit(1);
2727         }
2728       argc -= 2;
2729       argv += 2;
2730     }
2731   if (mainmode == MODE_SEARCH)
2732     {
2733       Dataiterator di;
2734       Map m;
2735       if (argc != 2)
2736         usage(1);
2737       map_init(&m, pool->nsolvables);
2738       dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
2739       dataiterator_set_keyname(&di, SOLVABLE_NAME);
2740       dataiterator_set_search(&di, 0, 0);
2741       while (dataiterator_step(&di))
2742         MAPSET(&m, di.solvid);
2743       dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
2744       dataiterator_set_search(&di, 0, 0);
2745       while (dataiterator_step(&di))
2746         MAPSET(&m, di.solvid);
2747       dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
2748       dataiterator_set_search(&di, 0, 0);
2749       while (dataiterator_step(&di))
2750         MAPSET(&m, di.solvid);
2751       dataiterator_free(&di);
2752
2753       for (p = 1; p < pool->nsolvables; p++)
2754         {
2755           Solvable *s = pool_id2solvable(pool, p);
2756           if (!MAPTST(&m, p))
2757             continue;
2758           printf("  - %s: %s\n", solvable2str(pool, s), solvable_lookup_str(s, SOLVABLE_SUMMARY));
2759         }
2760       map_free(&m);
2761       exit(0);
2762     }
2763
2764
2765   if (mainmode == MODE_LIST || mainmode == MODE_INSTALL)
2766     {
2767       for (i = 1; i < argc; i++)
2768         {
2769           int l;
2770           l = strlen(argv[i]);
2771 #ifndef DEBIAN
2772           if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
2773             continue;
2774 #else
2775           if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
2776             continue;
2777 #endif
2778           if (access(argv[i], R_OK))
2779             {
2780               perror(argv[i]);
2781               exit(1);
2782             }
2783           if (!commandlinepkgs)
2784             commandlinepkgs = sat_calloc(argc, sizeof(Id));
2785           if (!commandlinerepo)
2786             commandlinerepo = repo_create(pool, "@commandline");
2787 #ifndef DEBIAN
2788           repo_add_rpms(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2789 #else
2790           repo_add_debs(commandlinerepo, (const char **)argv + i, 1, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
2791 #endif
2792           commandlinepkgs[i] = commandlinerepo->end - 1;
2793         }
2794       if (commandlinerepo)
2795         repo_internalize(commandlinerepo);
2796     }
2797
2798   // FOR_REPOS(i, repo)
2799   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
2800   addedfileprovides = 0;
2801   pool_addfileprovides_ids(pool, 0, &addedfileprovides);
2802   if (addedfileprovides && *addedfileprovides)
2803     rewrite_repos(pool, addedfileprovides);
2804   sat_free(addedfileprovides);
2805   pool_createwhatprovides(pool);
2806
2807   queue_init(&job);
2808   for (i = 1; i < argc; i++)
2809     {
2810       Queue job2;
2811       int j;
2812
2813       if (commandlinepkgs && commandlinepkgs[i])
2814         {
2815           queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
2816           continue;
2817         }
2818       queue_init(&job2);
2819       if (!mkselect(pool, mode, argv[i], &job2))
2820         {
2821           fprintf(stderr, "nothing matches '%s'\n", argv[i]);
2822           exit(1);
2823         }
2824       if (repofilter && !limitrepo(pool, repofilter, &job2))
2825         {
2826           fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]);
2827           exit(1);
2828         }
2829       for (j = 0; j < job2.count; j++)
2830         queue_push(&job, job2.elements[j]);
2831       queue_free(&job2);
2832     }
2833
2834   if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH)
2835     {
2836       printf("no package matched\n");
2837       exit(1);
2838     }
2839
2840   if (!job.count)
2841     allpkgs = 1;
2842
2843   if (mainmode == MODE_LIST || mainmode == MODE_INFO)
2844     {
2845       /* list mode, no solver needed */
2846       for (i = 0; i < job.count; i += 2)
2847         {
2848           Id how = job.elements[i] & SOLVER_SELECTMASK;
2849           FOR_JOB_SELECT(p, pp, how, job.elements[i + 1])
2850             {
2851               Solvable *s = pool_id2solvable(pool, p);
2852               if (mainmode == MODE_INFO)
2853                 {
2854                   const char *str;
2855                   printf("Name:        %s\n", solvable2str(pool, s));
2856                   printf("Repo:        %s\n", s->repo->name);
2857                   printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
2858                   str = solvable_lookup_str(s, SOLVABLE_URL);
2859                   if (str)
2860                     printf("Url:         %s\n", str);
2861                   str = solvable_lookup_str(s, SOLVABLE_LICENSE);
2862                   if (str)
2863                     printf("License:     %s\n", str);
2864                   printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
2865                   printf("\n");
2866                 }
2867               else
2868                 {
2869 #if 1
2870                   const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
2871 #else
2872                   const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
2873 #endif
2874                   printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
2875                   if (sum)
2876                     printf("    %s\n", sum);
2877                 }
2878             }
2879         }
2880       queue_free(&job);
2881       pool_free(pool);
2882       free_repoinfos(repoinfos, nrepoinfos);
2883       sat_free(commandlinepkgs);
2884 #ifdef FEDORA
2885       yum_substitute(pool, 0);
2886 #endif
2887       exit(0);
2888     }
2889
2890   if (mainmode == MODE_PATCH)
2891     {
2892       int pruneyou = 0;
2893       Map installedmap;
2894       Solvable *s;
2895
2896       map_init(&installedmap, pool->nsolvables);
2897       if (pool->installed)
2898         FOR_REPO_SOLVABLES(pool->installed, p, s)
2899           MAPSET(&installedmap, p);
2900
2901       /* install all patches */
2902       for (p = 1; p < pool->nsolvables; p++)
2903         {
2904           const char *type;
2905           int r;
2906           Id p2;
2907
2908           s = pool->solvables + p;
2909           if (strncmp(id2str(pool, s->name), "patch:", 6) != 0)
2910             continue;
2911           FOR_PROVIDES(p2, pp, s->name)
2912             {
2913               Solvable *s2 = pool->solvables + p2;
2914               if (s2->name != s->name)
2915                 continue;
2916               r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
2917               if (r < 0 || (r == 0 && p > p2))
2918                 break;
2919             }
2920           if (p2)
2921             continue;
2922           type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
2923           if (type && !strcmp(type, "optional"))
2924             continue;
2925           r = solvable_trivial_installable_map(s, &installedmap, 0);
2926           if (r == -1)
2927             continue;
2928           if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
2929             {
2930               if (!pruneyou++)
2931                 queue_empty(&job);
2932             }
2933           else if (pruneyou)
2934             continue;
2935           queue_push2(&job, SOLVER_SOLVABLE, p);
2936         }
2937       map_free(&installedmap);
2938     }
2939
2940   // add mode
2941   for (i = 0; i < job.count; i += 2)
2942     {
2943       if (mode == SOLVER_UPDATE)
2944         {
2945           /* make update of not installed packages an install */
2946           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
2947             if (pool->installed && pool->solvables[p].repo == pool->installed)
2948               break;
2949           if (!p)
2950             {
2951               job.elements[i] |= SOLVER_INSTALL;
2952               continue;
2953             }
2954         }
2955       job.elements[i] |= mode;
2956       if (mainmode == MODE_ERASECLEAN)
2957         job.elements[i] |= SOLVER_CLEANDEPS;
2958     }
2959
2960   if (mainmode == MODE_DISTUPGRADE && allpkgs && repofilter)
2961     queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter);
2962
2963   // multiversion test
2964   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae", 1));
2965   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-base", 1));
2966   // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, str2id(pool, "kernel-pae-extra", 1));
2967
2968 #ifdef SOFTLOCKS_PATH
2969   addsoftlocks(pool, &job);
2970 #endif
2971
2972 #ifndef DEBIAN
2973 rerunsolver:
2974 #endif
2975   for (;;)
2976     {
2977       Id problem, solution;
2978       int pcnt, scnt;
2979
2980       solv = solver_create(pool);
2981       solv->ignorealreadyrecommended = 1;
2982       solv->updatesystem = allpkgs && !repofilter && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE);
2983       solv->dosplitprovides = solv->updatesystem;
2984       solv->fixsystem = allpkgs && !repofilter && mainmode == MODE_VERIFY;
2985       if (mainmode == MODE_DISTUPGRADE && allpkgs && !repofilter)
2986         {
2987           solv->distupgrade = 1;
2988           solv->allowdowngrade = 1;
2989           solv->allowarchchange = 1;
2990           solv->allowvendorchange = 1;
2991         }
2992       if (mainmode == MODE_ERASE || mainmode == MODE_ERASECLEAN)
2993         solv->allowuninstall = 1;       /* don't nag */
2994
2995       solver_solve(solv, &job);
2996       if (!solv->problems.count)
2997         break;
2998       pcnt = solver_problem_count(solv);
2999       printf("Found %d problems:\n", pcnt);
3000       for (problem = 1; problem <= pcnt; problem++)
3001         {
3002           int take = 0;
3003           printf("Problem %d:\n", problem);
3004           solver_printprobleminfo(solv, problem);
3005           printf("\n");
3006           scnt = solver_solution_count(solv, problem);
3007           for (solution = 1; solution <= scnt; solution++)
3008             {
3009               printf("Solution %d:\n", solution);
3010               solver_printsolution(solv, problem, solution);
3011               printf("\n");
3012             }
3013           for (;;)
3014             {
3015               printf("Please choose a solution: ");
3016               fflush(stdout);
3017               *inbuf = 0;
3018               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
3019                 {
3020                   printf("Abort.\n");
3021                   exit(1);
3022                 }
3023               while (*ip == ' ' || *ip == '\t')
3024                 ip++;
3025               if (*ip >= '0' && *ip <= '9')
3026                 {
3027                   take = atoi(ip);
3028                   if (take >= 1 && take <= scnt)
3029                     break;
3030                 }
3031               if (*ip == 's')
3032                 {
3033                   take = 0;
3034                   break;
3035                 }
3036               if (*ip == 'q')
3037                 {
3038                   printf("Abort.\n");
3039                   exit(1);
3040                 }
3041             }
3042           if (!take)
3043             continue;
3044           solver_take_solution(solv, problem, take, &job);
3045         }
3046       solver_free(solv);
3047       solv = 0;
3048     }
3049
3050   trans = &solv->trans;
3051   if (!trans->steps.count)
3052     {
3053       printf("Nothing to do.\n");
3054       solver_free(solv);
3055       queue_free(&job);
3056       pool_free(pool);
3057       free_repoinfos(repoinfos, nrepoinfos);
3058       sat_free(commandlinepkgs);
3059 #ifdef FEDORA
3060       yum_substitute(pool, 0);
3061 #endif
3062       exit(1);
3063     }
3064   printf("\n");
3065   printf("Transaction summary:\n\n");
3066   solver_printtransaction(solv);
3067
3068 #if !defined(FEDORA) && !defined(DEBIAN)
3069   if (1)
3070     {
3071       DUChanges duc[4];
3072       int i;
3073
3074       duc[0].path = "/";
3075       duc[1].path = "/usr/share/man";
3076       duc[2].path = "/sbin";
3077       duc[3].path = "/etc";
3078       transaction_calc_duchanges(trans, duc, 4);
3079       for (i = 0; i < 4; i++)
3080         printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
3081     }
3082 #endif
3083   printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
3084   printf("\n");
3085
3086   if (!yesno("OK to continue (y/n)? "))
3087     {
3088       printf("Abort.\n");
3089       solver_free(solv);
3090       queue_free(&job);
3091       pool_free(pool);
3092       free_repoinfos(repoinfos, nrepoinfos);
3093       sat_free(commandlinepkgs);
3094 #ifdef FEDORA
3095       yum_substitute(pool, 0);
3096 #endif
3097       exit(1);
3098     }
3099
3100   queue_init(&checkq);
3101   newpkgs = transaction_installedresult(trans, &checkq);
3102   newpkgsfps = 0;
3103
3104   if (newpkgs)
3105     {
3106       int downloadsize = 0;
3107       for (i = 0; i < newpkgs; i++)
3108         {
3109           Solvable *s;
3110
3111           p = checkq.elements[i];
3112           s = pool_id2solvable(pool, p);
3113           downloadsize += solvable_lookup_num(s, SOLVABLE_DOWNLOADSIZE, 0);
3114         }
3115       printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
3116       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
3117       for (i = 0; i < newpkgs; i++)
3118         {
3119           unsigned int medianr;
3120           char *loc;
3121           Solvable *s;
3122           struct repoinfo *cinfo;
3123           const unsigned char *chksum;
3124           Id chksumtype;
3125           Dataiterator di;
3126
3127           p = checkq.elements[i];
3128           s = pool_id2solvable(pool, p);
3129           if (s->repo == commandlinerepo)
3130             {
3131               loc = solvable_get_location(s, &medianr);
3132               if (!(newpkgsfps[i] = fopen(loc, "r")))
3133                 {
3134                   perror(loc);
3135                   exit(1);
3136                 }
3137               putchar('.');
3138               continue;
3139             }
3140           cinfo = s->repo->appdata;
3141           if (!cinfo)
3142             {
3143               printf("%s: no repository information\n", s->repo->name);
3144               exit(1);
3145             }
3146           loc = solvable_get_location(s, &medianr);
3147           if (!loc)
3148              continue;
3149
3150           if (pool->installed && pool->installed->nsolvables)
3151             {
3152               /* try a delta first */
3153               char *matchname = strdup(id2str(pool, s->name));
3154               dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
3155               dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
3156               while (dataiterator_step(&di))
3157                 {
3158                   Id baseevr, op;
3159
3160                   dataiterator_setpos_parent(&di);
3161                   if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
3162                       pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
3163                     continue;
3164                   baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
3165                   FOR_PROVIDES(op, pp, s->name)
3166                     {
3167                       Solvable *os = pool->solvables + op;
3168                       if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
3169                         break;
3170                     }
3171                   if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
3172                     {
3173                       /* base is installed, run sequence check */
3174                       const char *seqname;
3175                       const char *seqevr;
3176                       const char *seqnum;
3177                       const char *seq;
3178                       const char *dloc;
3179                       FILE *fp;
3180                       char cmd[128];
3181                       int newfd;
3182
3183                       seqname = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME);
3184                       seqevr = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR);
3185                       seqnum = pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM);
3186                       seq = pool_tmpjoin(pool, seqname, "-", seqevr);
3187                       seq = pool_tmpjoin(pool, seq, "-", seqnum);
3188 #ifdef FEDORA
3189                       sprintf(cmd, "/usr/bin/applydeltarpm -a %s -c -s ", id2str(pool, s->arch));
3190 #else
3191                       sprintf(cmd, "/usr/bin/applydeltarpm -c -s ");
3192 #endif
3193                       if (system(pool_tmpjoin(pool, cmd, seq, 0)) != 0)
3194                         continue;       /* didn't match */
3195                       /* looks good, download delta */
3196                       chksumtype = 0;
3197                       chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
3198                       if (!chksumtype)
3199                         continue;       /* no way! */
3200                       dloc = pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_DIR);
3201                       dloc = pool_tmpjoin(pool, dloc, "/", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_NAME));
3202                       dloc = pool_tmpjoin(pool, dloc, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_EVR));
3203                       dloc = pool_tmpjoin(pool, dloc, ".", pool_lookup_str(pool, SOLVID_POS, DELTA_LOCATION_SUFFIX));
3204                       if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
3205                         continue;
3206                       /* got it, now reconstruct */
3207                       newfd = opentmpfile();
3208 #ifdef FEDORA
3209                       sprintf(cmd, "applydeltarpm -a %s /dev/fd/%d /dev/fd/%d", id2str(pool, s->arch), fileno(fp), newfd);
3210 #else
3211                       sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
3212 #endif
3213                       fcntl(fileno(fp), F_SETFD, 0);
3214                       if (system(cmd))
3215                         {
3216                           close(newfd);
3217                           fclose(fp);
3218                           continue;
3219                         }
3220                       lseek(newfd, 0, SEEK_SET);
3221                       chksumtype = 0;
3222                       chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3223                       if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
3224                         {
3225                           close(newfd);
3226                           fclose(fp);
3227                           continue;
3228                         }
3229                       newpkgsfps[i] = fdopen(newfd, "r");
3230                       fclose(fp);
3231                       break;
3232                     }
3233                 }
3234               dataiterator_free(&di);
3235               sat_free(matchname);
3236             }
3237           
3238           if (newpkgsfps[i])
3239             {
3240               putchar('d');
3241               fflush(stdout);
3242               continue;         /* delta worked! */
3243             }
3244           if (cinfo->type == TYPE_SUSETAGS)
3245             {
3246               const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
3247               loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
3248             }
3249           chksumtype = 0;
3250           chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
3251           if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
3252             {
3253               printf("\n%s: %s not found in repository\n", s->repo->name, loc);
3254               exit(1);
3255             }
3256           putchar('.');
3257           fflush(stdout);
3258         }
3259       putchar('\n');
3260     }
3261
3262 #ifndef DEBIAN
3263   if (newpkgs)
3264     {
3265       Queue conflicts;
3266       struct fcstate fcstate;
3267
3268       printf("Searching for file conflicts\n");
3269       queue_init(&conflicts);
3270       fcstate.rpmdbstate = 0;
3271       fcstate.newpkgscnt = newpkgs;
3272       fcstate.checkq = &checkq;
3273       fcstate.newpkgsfps = newpkgsfps;
3274       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fileconflict_cb, &fcstate);
3275       if (conflicts.count)
3276         {
3277           printf("\n");
3278           for (i = 0; i < conflicts.count; i += 5)
3279             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]));
3280           printf("\n");
3281           if (yesno("Re-run solver (y/n/q)? "))
3282             {
3283               for (i = 0; i < newpkgs; i++)
3284                 if (newpkgsfps[i])
3285                   fclose(newpkgsfps[i]);
3286               newpkgsfps = sat_free(newpkgsfps);
3287               solver_free(solv);
3288               pool_add_fileconflicts_deps(pool, &conflicts);
3289               pool_createwhatprovides(pool);    /* Hmm... */
3290               goto rerunsolver;
3291             }
3292         }
3293       queue_free(&conflicts);
3294     }
3295 #endif
3296
3297   printf("Committing transaction:\n\n");
3298   transaction_order(trans, 0);
3299   for (i = 0; i < trans->steps.count; i++)
3300     {
3301 #ifndef DEBIAN
3302       const char *evr, *evrp, *nvra;
3303 #endif
3304       Solvable *s;
3305       int j;
3306       FILE *fp;
3307
3308       p = trans->steps.elements[i];
3309       s = pool_id2solvable(pool, p);
3310       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
3311       switch(type)
3312         {
3313         case SOLVER_TRANSACTION_ERASE:
3314           printf("erase %s\n", solvid2str(pool, p));
3315 #ifndef DEBIAN
3316           if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
3317             continue;
3318           /* strip epoch from evr */
3319           evr = evrp = id2str(pool, s->evr);
3320           while (*evrp >= '0' && *evrp <= '9')
3321             evrp++;
3322           if (evrp > evr && evrp[0] == ':' && evrp[1])
3323             evr = evrp + 1;
3324           nvra = pool_tmpjoin(pool, id2str(pool, s->name), "-", evr);
3325           nvra = pool_tmpjoin(pool, nvra, ".", id2str(pool, s->arch));
3326           runrpm("-e", nvra, -1);       /* too bad that --querybynumber doesn't work */
3327 #else
3328           rundpkg("--remove", id2str(pool, s->name), 0);
3329 #endif
3330           break;
3331         case SOLVER_TRANSACTION_INSTALL:
3332         case SOLVER_TRANSACTION_MULTIINSTALL:
3333           printf("install %s\n", solvid2str(pool, p));
3334           for (j = 0; j < newpkgs; j++)
3335             if (checkq.elements[j] == p)
3336               break;
3337           fp = j < newpkgs ? newpkgsfps[j] : 0;
3338           if (!fp)
3339             continue;
3340           rewind(fp);
3341           lseek(fileno(fp), 0, SEEK_SET);
3342 #ifndef DEBIAN
3343           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
3344 #else
3345           rundpkg("--install", "/dev/fd/3", fileno(fp));
3346 #endif
3347           fclose(fp);
3348           newpkgsfps[j] = 0;
3349           break;
3350         default:
3351           break;
3352         }
3353     }
3354
3355   for (i = 0; i < newpkgs; i++)
3356     if (newpkgsfps[i])
3357       fclose(newpkgsfps[i]);
3358   sat_free(newpkgsfps);
3359   queue_free(&checkq);
3360   solver_free(solv);
3361   queue_free(&job);
3362   pool_free(pool);
3363   free_repoinfos(repoinfos, nrepoinfos);
3364   sat_free(commandlinepkgs);
3365 #ifdef FEDORA
3366   yum_substitute(pool, 0);
3367 #endif
3368   exit(0);
3369 }