- ignore version in source field
[platform/upstream/libsolv.git] / ext / repo_deb.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 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <zlib.h>
15
16 #include "pool.h"
17 #include "repo.h"
18 #include "util.h"
19 #include "chksum.h"
20 #include "repo_deb.h"
21
22 static unsigned char *
23 decompress(unsigned char *in, int inl, int *outlp)
24 {
25   z_stream strm;
26   int outl, ret;
27   unsigned char *out;
28
29   memset(&strm, 0, sizeof(strm));
30   strm.next_in = in;
31   strm.avail_in = inl;
32   out = sat_malloc(4096);
33   strm.next_out = out;
34   strm.avail_out = 4096;
35   outl = 0;
36   ret = inflateInit2(&strm, -MAX_WBITS);
37   if (ret != Z_OK)
38     {
39       free(out);
40       return 0;
41     }
42   for (;;)
43     {
44       if (strm.avail_out == 0)
45         {
46           outl += 4096;
47           out = sat_realloc(out, outl + 4096);
48           strm.next_out = out + outl;
49           strm.avail_out = 4096;
50         }
51       ret = inflate(&strm, Z_NO_FLUSH);
52       if (ret == Z_STREAM_END)
53         break;
54       if (ret != Z_OK)
55         {
56           free(out);
57           return 0;
58         }
59     }
60   outl += 4096 - strm.avail_out;
61   inflateEnd(&strm);
62   *outlp = outl;
63   return out;
64 }
65
66 static Id
67 parseonedep(Pool *pool, char *p)
68 {
69   char *n, *ne, *e, *ee;
70   Id name, evr;
71   int flags;
72
73   while (*p == ' ' || *p == '\t' || *p == '\n')
74     p++;
75   if (!*p || *p == '(')
76     return 0;
77   n = p;
78   /* find end of name */
79   while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
80     p++;
81   ne = p;
82   while (*p == ' ' || *p == '\t' || *p == '\n')
83     p++;
84   evr = 0;
85   flags = 0;
86   e = ee = 0;
87   if (*p == '(')
88     {
89       p++;
90       while (*p == ' ' || *p == '\t' || *p == '\n')
91         p++;
92       if (*p == '>')
93         flags |= REL_GT;
94       else if (*p == '=')
95         flags |= REL_EQ;
96       else if (*p == '<')
97         flags |= REL_LT;
98       if (flags)
99         {
100           p++;
101           if (*p == '>')
102             flags |= REL_GT;
103           else if (*p == '=')
104             flags |= REL_EQ;
105           else if (*p == '<')
106             flags |= REL_LT;
107           else
108             p--;
109           p++;
110         }
111       while (*p == ' ' || *p == '\t' || *p == '\n')
112         p++;
113       e = p;
114       while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
115         p++;
116       ee = p;
117       while (*p && *p != ')')
118         p++;
119       if (*p)
120         p++;
121       while (*p == ' ' || *p == '\t' || *p == '\n')
122         p++;
123     }
124   name = strn2id(pool, n, ne - n, 1);
125   if (e)
126     {
127       evr = strn2id(pool, e, ee - e, 1);
128       name = rel2id(pool, name, evr, flags, 1);
129     }
130   if (*p == '|')
131     {
132       Id id = parseonedep(pool, p + 1);
133       if (id)
134         name = rel2id(pool, name, id, REL_OR, 1);
135     }
136   return name;
137 }
138
139 static unsigned int
140 makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
141 {
142   Pool *pool = repo->pool;
143   char *p;
144   Id id;
145
146   while ((p = strchr(deps, ',')) != 0)
147     {
148       *p = 0;
149       olddeps = makedeps(repo, deps, olddeps, marker);
150       *p = ',';
151       deps = p + 1;
152     }
153   id = parseonedep(pool, deps);
154   if (!id)
155     return olddeps;
156   return repo_addid_dep(repo, olddeps, id, marker);
157 }
158
159
160 /* put data from control file into the solvable */
161 /* warning: does inplace changes */
162 static void
163 control2solvable(Solvable *s, Repodata *data, char *control)
164 {
165   Repo *repo = s->repo;
166   Pool *pool = repo->pool;
167   char *p, *q, *end, *tag;
168   int x, l;
169   int havesource = 0;
170
171   p = control;
172   while (*p)
173     {
174       p = strchr(p, '\n');
175       if (!p)
176         break;
177       if (p[1] == ' ' || p[1] == '\t')
178         {
179           char *q;
180           /* continuation line */
181           q = p - 1;
182           while (q >= control && *q == ' ' && *q == '\t')
183             q--;
184           l = q + 1 - control;
185           if (l)
186             memmove(p + 1 - l, control, l);
187           control = p + 1 - l;
188           p[1] = '\n';
189           p += 2;
190           continue;
191         }
192       end = p - 1;
193       if (*p)
194         *p++ = 0;
195       /* strip trailing space */
196       while (end >= control && *end == ' ' && *end == '\t')
197         *end-- = 0;
198       tag = control;
199       control = p;
200       q = strchr(tag, ':');
201       if (!q || q - tag < 4)
202         continue;
203       *q++ = 0;
204       while (*q == ' ' || *q == '\t')
205         q++;
206       x = '@' + (tag[0] & 0x1f);
207       x = (x << 8) + '@' + (tag[1] & 0x1f);
208       switch(x)
209         {
210         case 'A' << 8 | 'R':
211           if (!strcasecmp(tag, "architecture"))
212             s->arch = str2id(pool, q, 1);
213           break;
214         case 'B' << 8 | 'R':
215           if (!strcasecmp(tag, "breaks"))
216             s->conflicts = makedeps(repo, q, s->conflicts, 0);
217           break;
218         case 'C' << 8 | 'O':
219           if (!strcasecmp(tag, "conflicts"))
220             s->conflicts = makedeps(repo, q, s->conflicts, 0);
221           break;
222         case 'D' << 8 | 'E':
223           if (!strcasecmp(tag, "depends"))
224             s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
225           else if (!strcasecmp(tag, "description"))
226             {
227               char *ld = strchr(q, '\n');
228               if (ld)
229                 {
230                   *ld++ = 0;
231                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
232                 }
233               else
234                 repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
235               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
236             }
237           break;
238         case 'E' << 8 | 'N':
239           if (!strcasecmp(tag, "enhances"))
240             s->enhances = makedeps(repo, q, s->enhances, 0);
241           break;
242         case 'H' << 8 | 'O':
243           if (!strcasecmp(tag, "homepage"))
244             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
245           break;
246         case 'I' << 8 | 'N':
247           if (!strcasecmp(tag, "installed-size"))
248             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, atoi(q));
249           break;
250         case 'P' << 8 | 'A':
251           if (!strcasecmp(tag, "package"))
252             s->name = str2id(pool, q, 1);
253           break;
254         case 'P' << 8 | 'R':
255           if (!strcasecmp(tag, "pre-depends"))
256             s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
257           else if (!strcasecmp(tag, "provides"))
258             s->provides = makedeps(repo, q, s->provides, 0);
259           break;
260         case 'R' << 8 | 'E':
261           if (!strcasecmp(tag, "replaces"))
262             s->obsoletes = makedeps(repo, q, s->conflicts, 0);
263           else if (!strcasecmp(tag, "recommends"))
264             s->recommends = makedeps(repo, q, s->recommends, 0);
265           break;
266         case 'S' << 8 | 'O':
267           if (!strcasecmp(tag, "source"))
268             {
269               char *q2;
270               /* ignore version for now */
271               for (q2 = q; *q2; q2++)
272                 if (*q2 == ' ' || *q2 == '\t')
273                   {
274                     *q2 = 0;
275                     break;
276                   }
277               if (s->name && !strcmp(q, id2str(pool, s->name)))
278                 repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
279               else
280                 repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, str2id(pool, q, 1));
281               havesource = 1;
282             }
283           break;
284         case 'S' << 8 | 'U':
285           if (!strcasecmp(tag, "suggests"))
286             s->suggests = makedeps(repo, q, s->suggests, 0);
287           break;
288         case 'V' << 8 | 'E':
289           if (!strcasecmp(tag, "version"))
290             s->evr = str2id(pool, q, 1);
291           break;
292         }
293     }
294   if (!s->arch)
295     s->arch = ARCH_ALL;
296   if (!s->evr)
297     s->evr = ID_EMPTY;
298   if (s->name)
299     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
300   if (s->name && !havesource)
301     repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
302 }
303
304 void
305 repo_add_debpackages(Repo *repo, FILE *fp, int flags)
306 {
307   Pool *pool = repo->pool;
308   Repodata *data;
309   char *buf, *p;
310   int bufl, l, ll;
311   Solvable *s;
312
313   data = repo_add_repodata(repo, flags);
314   buf = sat_malloc(4096);
315   bufl = 4096;
316   l = 0;
317   buf[l] = 0;
318   p = buf;
319   for (;;)
320     {
321       if (!(p = strchr(p, '\n')))
322         {
323           int l3;
324           if (l + 1024 >= bufl)
325             {
326               buf = sat_realloc(buf, bufl + 4096);
327               bufl += 4096;
328               p = buf + l;
329               continue;
330             }
331           p = buf + l;
332           ll = fread(p, 1, bufl - l - 1, fp);
333           if (ll <= 0)
334             break;
335           p[ll] = 0;
336           while ((l3 = strlen(p)) < ll)
337             p[l3] = '\n';
338           l += ll;
339           continue;
340         }
341       p++;
342       if (*p != '\n')
343         continue;
344       *p = 0;
345       ll = p - buf + 1;
346       s = pool_id2solvable(pool, repo_add_solvable(repo));
347       control2solvable(s, data, buf);
348       if (!s->name)
349         repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
350       if (l > ll)
351         memmove(buf, p + 1, l - ll);
352       l -= ll;
353       p = buf;
354       buf[l] = 0;
355     }
356   if (l)
357     {
358       s = pool_id2solvable(pool, repo_add_solvable(repo));
359       control2solvable(s, data, buf);
360       if (!s->name)
361         repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
362     }
363   sat_free(buf);
364   if (!(flags & REPO_NO_INTERNALIZE))
365     repodata_internalize(data);
366 }
367
368 void
369 repo_add_debs(Repo *repo, const char **debs, int ndebs, int flags)
370 {
371   Pool *pool = repo->pool;
372   Repodata *data;
373   unsigned char buf[4096], *bp;
374   int i, l, l2, vlen, clen, ctarlen;
375   unsigned char *ctgz;
376   unsigned char pkgid[16];
377   unsigned char *ctar;
378   int gotpkgid;
379   FILE *fp;
380   Solvable *s;
381   struct stat stb;
382
383   data = repo_add_repodata(repo, flags);
384   for (i = 0; i < ndebs; i++)
385     {
386       if ((fp = fopen(debs[i], "r")) == 0)
387         {
388           perror(debs[i]);
389           continue;
390         }
391       if (fstat(fileno(fp), &stb))
392         {
393           perror("stat");
394           continue;
395         }
396       l = fread(buf, 1, sizeof(buf), fp);
397       if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0)
398         {
399           fprintf(stderr, "%s: not a deb package\n", debs[i]);
400           fclose(fp);
401           continue;
402         }
403       vlen = atoi((char *)buf + 8 + 48);
404       if (vlen < 0 || vlen > l)
405         {
406           fprintf(stderr, "%s: not a deb package\n", debs[i]);
407           fclose(fp);
408           continue;
409         }
410       vlen += vlen & 1;
411       if (l < 8 + 60 + vlen + 60)
412         {
413           fprintf(stderr, "%s: unhandled deb package\n", debs[i]);
414           fclose(fp);
415           continue;
416         }
417       if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0)
418         {
419           fprintf(stderr, "%s: control.tar.gz is not second entry\n", debs[i]);
420           fclose(fp);
421           continue;
422         }
423       clen = atoi((char *)buf + 8 + 60 + vlen + 48);
424       if (clen <= 0)
425         {
426           fprintf(stderr, "%s: control.tar.gz has illegal size\n", debs[i]);
427           fclose(fp);
428           continue;
429         }
430       ctgz = sat_calloc(1, clen + 4);
431       bp = buf + 8 + 60 + vlen + 60;
432       l -= 8 + 60 + vlen + 60;
433       if (l > clen)
434         l = clen;
435       if (l)
436         memcpy(ctgz, bp, l);
437       if (l < clen)
438         {
439           if (fread(ctgz + l, clen - l, 1, fp) != 1)
440             {
441               fprintf(stderr, "%s: unexpected EOF\n", debs[i]);
442               sat_free(ctgz);
443               fclose(fp);
444               continue;
445             }
446         }
447       fclose(fp);
448       gotpkgid = 0;
449       if (flags & DEBS_ADD_WITH_PKGID)
450         {
451           void *handle = sat_chksum_create(REPOKEY_TYPE_MD5);
452           sat_chksum_add(handle, ctgz, clen);
453           sat_chksum_free(handle, pkgid);
454           gotpkgid = 1;
455         }
456       if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
457         {
458           fprintf(stderr, "%s: control.tar.gz is not gzipped\n", debs[i]);
459           sat_free(ctgz);
460           continue;
461         }
462       if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
463         {
464           fprintf(stderr, "%s: control.tar.gz is compressed in a strange way\n", debs[i]);
465           sat_free(ctgz);
466           continue;
467         }
468       bp = ctgz + 4;
469       bp += 6;  /* skip time, xflags and OS code */
470       if (ctgz[3] & 0x04)
471         {
472           /* skip extra field */
473           l = bp[0] | bp[1] << 8;
474           bp += l + 2;
475           if (bp >= ctgz + clen)
476             {
477               fprintf(stderr, "%s: corrupt gzip\n", debs[i]);
478               sat_free(ctgz);
479               continue;
480             }
481         }
482       if (ctgz[3] & 0x08)       /* orig filename */
483         while (*bp)
484           bp++;
485       if (ctgz[3] & 0x10)       /* file comment */
486         while (*bp)
487           bp++;
488       if (ctgz[3] & 0x02)       /* header crc */
489         bp += 2;
490       if (bp >= ctgz + clen)
491         {
492           fprintf(stderr, "%s: corrupt control.tar.gz\n", debs[i]);
493           sat_free(ctgz);
494           continue;
495         }
496       ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
497       sat_free(ctgz);
498       if (!ctar)
499         {
500           fprintf(stderr, "%s: corrupt control.tar.gz\n", debs[i]);
501           continue;
502         }
503       bp = ctar;
504       l = ctarlen;
505       while (l > 512)
506         {
507           int j;
508           l2 = 0;
509           for (j = 124; j < 124 + 12; j++)
510             if (bp[j] >= '0' && bp[j] <= '7')
511               l2 = l2 * 8 + (bp[j] - '0');
512           if (!strcmp((char *)bp, "./control"))
513             break;
514           l2 = 512 + ((l2 + 511) & ~511);
515           l -= l2;
516           bp += l2;
517         }
518       if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
519         {
520           fprintf(stderr, "%s: control.tar.gz contains no ./control file\n", debs[i]);
521           free(ctar);
522           continue;
523         }
524       memmove(ctar, bp + 512, l2);
525       ctar = sat_realloc(ctar, l2 + 1);
526       ctar[l2] = 0;
527       s = pool_id2solvable(pool, repo_add_solvable(repo));
528       control2solvable(s, data, (char *)ctar);
529       repodata_set_location(data, s - pool->solvables, 0, 0, debs[i]);
530       if (S_ISREG(stb.st_mode))
531         repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned int)((stb.st_size + 1023) / 1024));
532       if (gotpkgid)
533         repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
534       sat_free(ctar);
535     }
536   if (!(flags & REPO_NO_INTERNALIZE))
537     repodata_internalize(data);
538 }