more of these init variables
[platform/upstream/libsolv.git] / tools / repo_updateinfoxml.c
1
2 /*
3  * Copyright (c) 2007, Novell Inc.
4  *
5  * This program is licensed under the BSD license, read LICENSE.BSD
6  * for further information
7  */
8
9 #define _GNU_SOURCE
10 #include <sys/types.h>
11 #include <limits.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <expat.h>
17
18 #include "pool.h"
19 #include "repo.h"
20 #include "repo_patchxml.h"
21 #include "repo_rpmmd.h"
22 #include "tools_util.h"
23
24 //#define TESTMM
25
26 /*
27  * <updates>
28  *   <update from="rel-eng@fedoraproject.org" status="stable" type="security" version="1.4">
29  *     <id>FEDORA-2007-4594</id>
30  *     <title>imlib-1.9.15-6.fc8</title>
31  *     <release>Fedora 8</release>
32  *     <issued date="2007-12-28 16:42:30"/>
33  *     <references>
34  *       <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=426091" id="426091" title="CVE-2007-3568 imlib: infinite loop DoS using crafted BMP image" type="bugzilla"/>
35  *     </references>
36  *     <description>This update includes a fix for a denial-of-service issue (CVE-2007-3568) whereby an attacker who could get an imlib-using user to view a  specially-crafted BMP image could cause the user's CPU to go into an infinite loop.</description>
37  *     <pkglist>
38  *       <collection short="F8">
39  *         <name>Fedora 8</name>
40  *         <package arch="ppc64" name="imlib-debuginfo" release="6.fc8" src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm" version="1.9.15">
41  *           <filename>imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm</filename>
42  *           <reboot_suggested>True</reboot_suggested>
43  *         </package>
44  *       </collection>
45  *     </pkglist>
46  *   </update>
47  * </updates>
48 */
49
50 enum state {
51   STATE_START,
52   STATE_UPDATES,      /* 1 */
53   STATE_UPDATE,       /* 2 */
54   STATE_ID,           /* 3 */
55   STATE_TITLE,        /* 4 */
56   STATE_RELEASE,      /* 5 */
57   STATE_ISSUED,       /* 6 */
58   STATE_REFERENCES,   /* 7 */
59   STATE_REFERENCE,    /* 8 */
60   STATE_DESCRIPTION,  /* 9 */
61   STATE_PKGLIST,     /* 10 */
62   STATE_COLLECTION,  /* 11 */
63   STATE_NAME,        /* 12 */
64   STATE_PACKAGE,     /* 13 */
65   STATE_FILENAME,    /* 14 */
66   STATE_REBOOT,      /* 15 */
67   NUMSTATES
68 };
69
70 struct stateswitch {
71   enum state from;
72   char *ename;
73   enum state to;
74   int docontent;
75 };
76
77
78 /* !! must be sorted by first column !! */
79 static struct stateswitch stateswitches[] = {
80   { STATE_START,       "updates",         STATE_UPDATES,     0 },
81   { STATE_START,       "update",          STATE_UPDATE,      0 },
82   { STATE_UPDATES,     "update",          STATE_UPDATE,      0 },
83   { STATE_UPDATE,      "id",              STATE_ID,          1 },
84   { STATE_UPDATE,      "title",           STATE_TITLE,       1 },
85   { STATE_UPDATE,      "release",         STATE_RELEASE,     1 },
86   { STATE_UPDATE,      "issued",          STATE_ISSUED,      1 },
87   { STATE_UPDATE,      "description",     STATE_DESCRIPTION, 1 },
88   { STATE_UPDATE,      "references",      STATE_REFERENCES,  0 },
89   { STATE_UPDATE,      "pkglist",         STATE_PKGLIST,     0 },
90   { STATE_REFERENCES,  "reference",       STATE_REFERENCE,   0 },
91   { STATE_PKGLIST,     "collection",      STATE_COLLECTION,  0 },
92   { STATE_COLLECTION,  "name",            STATE_NAME,        1 },
93   { STATE_COLLECTION,  "package",         STATE_PACKAGE,     0 },
94   { STATE_PACKAGE,     "filename",        STATE_FILENAME,    1 },
95   { STATE_PACKAGE,     "reboot_suggested",STATE_REBOOT,      1 },
96   { NUMSTATES }
97 };
98
99 struct parsedata {
100   int depth;
101   enum state state;
102   int statedepth;
103   char *content;
104   int lcontent;
105   int acontent;
106   int docontent;
107   Pool *pool;
108   Repo *repo;
109   Repodata *data;
110   unsigned int datanum;
111   Solvable *solvable;
112   unsigned int timestamp;
113   
114   struct stateswitch *swtab[NUMSTATES];
115   enum state sbtab[NUMSTATES];
116   char *tempstr;
117   int ltemp;
118   int atemp;
119 };
120
121 /*
122  * find attribute
123  */
124
125 /*
126 static const char *
127 find_attr(const char *txt, const char **atts)
128 {
129   for (; *atts; atts += 2)
130     {
131       if (!strcmp(*atts, txt))
132         return atts[1];
133     }
134   return 0;
135 }
136 */
137
138
139 /*
140  * create evr (as Id) from 'epoch', 'version' and 'release' attributes
141  */
142
143 static Id
144 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
145 {
146   const char *e, *v, *r, *v2;
147   char *c;
148   int l;
149
150   e = v = r = 0;
151   for (; *atts; atts += 2)
152     {
153       if (!strcmp(*atts, "epoch"))
154         e = atts[1];
155       else if (!strcmp(*atts, "version"))
156         v = atts[1];
157       else if (!strcmp(*atts, "release"))
158         r = atts[1];
159     }
160   if (e && !strcmp(e, "0"))
161     e = 0;
162   if (v && !e)
163     {
164       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
165         ;
166       if (v2 > v && *v2 == ':')
167         e = "0";
168     }
169   l = 1;
170   if (e)
171     l += strlen(e) + 1;
172   if (v)
173     l += strlen(v);
174   if (r)
175     l += strlen(r) + 1;
176   if (l > pd->acontent)
177     {
178       pd->content = realloc(pd->content, l + 256);
179       pd->acontent = l + 256;
180     }
181   c = pd->content;
182   if (e)
183     {
184       strcpy(c, e);
185       c += strlen(c);
186       *c++ = ':';
187     }
188   if (v)
189     {
190       strcpy(c, v);
191       c += strlen(c);
192     }
193   if (r)
194     {
195       *c++ = '-';
196       strcpy(c, r);
197       c += strlen(c);
198     }
199   *c = 0;
200   if (!*pd->content)
201     return 0;
202 #if 0
203   fprintf(stderr, "evr: %s\n", pd->content);
204 #endif
205   return str2id(pool, pd->content, 1);
206 }
207
208
209
210 static void XMLCALL
211 startElement(void *userData, const char *name, const char **atts)
212 {
213   struct parsedata *pd = userData;
214   Pool *pool = pd->pool;
215   Solvable *solvable = pd->solvable;
216   struct stateswitch *sw;
217   /*const char *str; */
218
219 #if 0
220       fprintf(stderr, "start: [%d]%s\n", pd->state, name);
221 #endif
222   if (pd->depth != pd->statedepth)
223     {
224       pd->depth++;
225       return;
226     }
227
228   pd->depth++;
229   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
230     if (!strcmp(sw->ename, name))
231       break;
232   
233   if (sw->from != pd->state)
234     {
235 #if 1
236       fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
237       exit( 1 );
238 #endif
239       return;
240     }
241   pd->state = sw->to;
242   pd->docontent = sw->docontent;
243   pd->statedepth = pd->depth;
244   pd->lcontent = 0;
245   *pd->content = 0;
246
247   switch(pd->state)
248     {
249       case STATE_START:
250       break;
251       case STATE_UPDATES:
252       break;
253       /*
254        * <update from="rel-eng@fedoraproject.org"
255        *         status="stable"
256        *         type="bugfix" (enhancement, security)
257        *         version="1.4">
258        */
259       case STATE_UPDATE:
260       {
261         const char *from = 0, *status = 0, *type = 0, *version = 0;
262         for (; *atts; atts += 2)
263         {
264           if (!strcmp(*atts, "from"))
265             from = atts[1];
266           else if (!strcmp(*atts, "status"))
267             status = atts[1];
268           else if (!strcmp(*atts, "type"))
269             type = atts[1];
270           else if (!strcmp(*atts, "version"))
271             version = atts[1];
272         }
273         
274         solvable = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
275         pd->datanum = (pd->solvable - pool->solvables) - pd->repo->start;
276         repodata_extend(pd->data, pd->solvable - pool->solvables);      
277         
278         solvable->vendor = str2id(pool, from, 1);
279         solvable->evr = str2id(pool, version, 1);
280         repodata_set_str(pd->data, pd->datanum, UPDATE_SEVERITY, type);
281       }
282       break;
283       /* <id>FEDORA-2007-4594</id> */
284       case STATE_ID:
285       break;
286       /* <title>imlib-1.9.15-6.fc8</title> */
287       case STATE_TITLE:
288       break;
289       /* <release>Fedora 8</release> */
290       case STATE_RELEASE:
291       break;
292       /*  <issued date="2008-03-21 21:36:55"/>
293       */
294       case STATE_ISSUED:
295       {
296         const char *date = 0;
297         for (; *atts; atts += 2)
298         {
299           if (!strcmp(*atts, "date"))
300             date = atts[1];
301         }
302         repodata_set_str(pd->data, pd->datanum, UPDATE_TIMESTAMP, date);
303       }
304       break;
305       case STATE_REFERENCES:
306       break;
307       /*  <reference href="https://bugzilla.redhat.com/show_bug.cgi?id=330471"
308        *             id="330471"
309        *             title="LDAP schema file missing for dhcpd"
310        *             type="bugzilla"/>
311        */
312       case STATE_REFERENCE:
313       break;
314       /* <description>This update ...</description> */
315       case STATE_DESCRIPTION:
316       break;
317       case STATE_PKGLIST:
318       break;
319       /* <collection short="F8"> */
320       case STATE_COLLECTION:
321       break;
322       /* <name>Fedora 8</name> */ 
323       case STATE_NAME:
324       break;
325       /*   <package arch="ppc64" name="imlib-debuginfo" release="6.fc8"
326        *            src="http://download.fedoraproject.org/pub/fedora/linux/updates/8/ppc64/imlib-debuginfo-1.9.15-6.fc8.ppc64.rpm"
327        *            version="1.9.15">
328        * 
329        * -> patch.conflicts: {name} < {version}.{release}
330        */
331       case STATE_PACKAGE:
332       {
333         const char *arch = 0, *name = 0, *src = 0;
334         Id evr = makeevr_atts(pool, pd, atts); /* parse "epoch", "version", "release" */
335         Id n;
336         Id rel_id;
337         for (; *atts; atts += 2)
338         {
339           if (!strcmp(*atts, "arch"))
340             arch = atts[1];
341           else if (!strcmp(*atts, "name"))
342             name = atts[1];
343           else if (!strcmp(*atts, "src"))
344             src = atts[1];
345         }
346         n = str2id(pool, name, 1);
347         rel_id = rel2id(pool, n, evr, REL_LT, 1);
348
349         solvable->conflicts = repo_addid_dep(pd->repo, solvable->conflicts, rel_id, 0);
350       }
351       break;
352       /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */ 
353       case STATE_FILENAME:
354       break;
355       /* <reboot_suggested>True</reboot_suggested> */
356       case STATE_REBOOT:
357       break;
358       case NUMSTATES+1:
359         split(NULL, NULL, 0); /* just to keep gcc happy about tools_util.h: static ... split() {...}  Urgs!*/
360       break;
361       default:
362       break;
363     }
364   return;
365 }
366
367
368 static void XMLCALL
369 endElement(void *userData, const char *name)
370 {
371   struct parsedata *pd = userData;
372   Pool *pool = pd->pool;
373   Solvable *s = pd->solvable;
374
375 #if 0
376       fprintf(stderr, "end: %s\n", name);
377 #endif
378   if (pd->depth != pd->statedepth)
379     {
380       pd->depth--;
381 #if 1
382       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
383 #endif
384       return;
385     }
386
387   pd->depth--;
388   pd->statedepth--;
389   switch (pd->state)
390     {
391       case STATE_START:
392       break;
393       case STATE_UPDATES:
394       break;
395       case STATE_UPDATE:
396       break;
397       case STATE_ID:
398       {
399         if (pd->content) {
400           s->name = str2id(pool, join2("patch", ":", pd->content), 1);
401         }
402       }
403       break;
404       /* <title>imlib-1.9.15-6.fc8</title> */
405       case STATE_TITLE:
406       {
407         while (pd->lcontent > 0
408                && *(pd->content + pd->lcontent - 1) == '\n')
409         {
410           --pd->lcontent;
411           *(pd->content + pd->lcontent) = 0;
412         }
413         repodata_set_str(pd->data, pd->datanum, SOLVABLE_SUMMARY, pd->content);
414       }
415       break;
416       /*
417        * <release>Fedora 8</release>
418        */
419       case STATE_RELEASE:
420       break;
421       case STATE_ISSUED:
422       break;
423       case STATE_REFERENCES:
424       break;
425       case STATE_REFERENCE:
426       break;
427       /*
428        * <description>This update ...</description>
429        */
430       case STATE_DESCRIPTION:
431       {
432         repodata_set_str(pd->data, pd->datanum, SOLVABLE_DESCRIPTION, pd->content);
433       }
434       break;   
435       case STATE_PKGLIST:
436       break;
437       case STATE_COLLECTION:
438       break;
439       case STATE_NAME:
440       break;
441       case STATE_PACKAGE:
442       break;
443       /* <filename>libntlm-0.4.2-1.fc8.x86_64.rpm</filename> */ 
444       case STATE_FILENAME:
445       break;
446       /* <reboot_suggested>True</reboot_suggested> */
447       case STATE_REBOOT:
448       {
449         repodata_set_str(pd->data, pd->datanum, UPDATE_REBOOT, pd->content);
450       }
451       break;
452       default:
453       break;
454     }
455
456   pd->state = pd->sbtab[pd->state];
457   pd->docontent = 0;
458   
459   return;
460 }
461
462
463 static void XMLCALL
464 characterData(void *userData, const XML_Char *s, int len)
465 {
466   struct parsedata *pd = userData;
467   int l;
468   char *c;
469   if (!pd->docontent) {
470 #if 0
471     char *dup = strndup( s, len );
472   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
473   free( dup );
474 #endif
475     return;
476   }
477   l = pd->lcontent + len + 1;
478   if (l > pd->acontent)
479     {
480       pd->content = realloc(pd->content, l + 256);
481       pd->acontent = l + 256;
482     }
483   c = pd->content + pd->lcontent;
484   pd->lcontent += len;
485   while (len-- > 0)
486     *c++ = *s++;
487   *c = 0;
488 }
489
490
491 #define BUFF_SIZE 8192
492
493 void
494 repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags)
495 {
496   Pool *pool = repo->pool;
497   struct parsedata pd;
498   char buf[BUFF_SIZE];
499   int i, l;
500   struct stateswitch *sw;
501
502   memset(&pd, 0, sizeof(pd));
503   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
504     {
505       if (!pd.swtab[sw->from])
506         pd.swtab[sw->from] = sw;
507       pd.sbtab[sw->to] = sw->from;
508     }
509   pd.pool = pool;
510   pd.repo = repo;
511   pd.data = repo_add_repodata(pd.repo, 0);
512
513   pd.content = malloc(256);
514   pd.acontent = 256;
515   pd.lcontent = 0;
516   pd.tempstr = malloc(256);
517   pd.atemp = 256;
518   pd.ltemp = 0;
519   XML_Parser parser = XML_ParserCreate(NULL);
520   XML_SetUserData(parser, &pd);
521   XML_SetElementHandler(parser, startElement, endElement);
522   XML_SetCharacterDataHandler(parser, characterData);
523   for (;;)
524     {
525       l = fread(buf, 1, sizeof(buf), fp);
526       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
527         {
528           fprintf(stderr, "repo_updateinfoxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
529           exit(1);
530         }
531       if (l == 0)
532         break;
533     }
534   XML_ParserFree(parser);
535
536   if (pd.data)
537     repodata_internalize(pd.data);
538
539   free(pd.content);
540 }
541
542 /* EOF */