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