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