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