- don't abort on unknown elements
[platform/upstream/libsolv.git] / tools / repo_repomdxml.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #define DO_ARRAY 1
9
10 #define _GNU_SOURCE
11 #include <sys/types.h>
12 #include <limits.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <expat.h>
18
19 #include "pool.h"
20 #include "repo.h"
21 #include "repo_updateinfoxml.h"
22
23 //#define DUMPOUT 0
24
25 /*
26 <repomd>
27
28   <!-- these tags are available in create repo > 0.9.6 -->
29   <revision>timestamp_or_arbitrary_user_supplied_string</revision>
30   <tags>
31     <content>opensuse</content>
32     <content>i386</content>
33     <content>other string</content>
34     <distro cpeid="cpe://o:opensuse_project:opensuse:11">openSUSE 11.0</distro>
35   </tags>
36   <!-- end -->
37
38   <data type="primary">
39     <location href="repodata/primary.xml.gz"/>
40     <checksum type="sha">e9162516fa25fec8d60caaf4682d2e49967786cc</checksum>
41     <timestamp>1215708444</timestamp>
42     <open-checksum type="sha">c796c48184cd5abc260e4ba929bdf01be14778a7</open-checksum>
43   </data>
44   <data type="filelists">
45     <location href="repodata/filelists.xml.gz"/>
46     <checksum type="sha">1c638295c49e9707c22810004ebb0799791fcf45</checksum>
47     <timestamp>1215708445</timestamp>
48     <open-checksum type="sha">54a40d5db3df0813b8acbe58cea616987eb9dc16</open-checksum>
49   </data>
50   <data type="other">
51     <location href="repodata/other.xml.gz"/>
52     <checksum type="sha">a81ef39eaa70e56048f8351055119d8c82af2491</checksum>
53     <timestamp>1215708447</timestamp>
54     <open-checksum type="sha">4d1ee867c8864025575a2fb8fde3b85371d51978</open-checksum>
55   </data>
56   <data type="deltainfo">
57     <location href="repodata/deltainfo.xml.gz"/>
58     <checksum type="sha">5880cfa5187026a24a552d3c0650904a44908c28</checksum>
59     <timestamp>1215708447</timestamp>
60     <open-checksum type="sha">7c964a2c3b17df5bfdd962c3be952c9ca6978d8b</open-checksum>
61   </data>
62   <data type="updateinfo">
63     <location href="repodata/updateinfo.xml.gz"/>
64     <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
65     <timestamp>1215708447</timestamp>
66     <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
67   </data>
68   <data type="diskusage">
69     <location href="repodata/diskusage.xml.gz"/>
70     <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
71     <timestamp>1215708447</timestamp>
72     <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
73   </data>
74 </repomd>
75
76 support also extension suseinfo format
77 <suseinfo>
78   <expire>timestamp</expire>
79   <products>
80     <id>...</id>
81   </products>
82   <kewwords>
83     <k>...</k>
84   </keywords>
85 </suseinfo>
86
87 */
88
89 enum state {
90   STATE_START,
91   /* extension tags */
92   STATE_SUSEINFO,
93   STATE_EXPIRE,
94   STATE_PRODUCTS,
95   STATE_PRODUCT,
96   STATE_KEYWORDS,
97   STATE_KEYWORD,
98   /* normal repomd.xml */
99   STATE_REPOMD,
100   STATE_REVISION,
101   STATE_TAGS,
102   STATE_CONTENT,
103   STATE_DISTRO,
104   STATE_DATA,
105   STATE_LOCATION,
106   STATE_CHECKSUM,
107   STATE_TIMESTAMP,
108   STATE_OPENCHECKSUM,
109   NUMSTATES
110 };
111
112 struct stateswitch {
113   enum state from;
114   char *ename;
115   enum state to;
116   int docontent;
117 };
118
119 /* !! must be sorted by first column !! */
120 static struct stateswitch stateswitches[] = {
121   /* suseinfo tags */
122   { STATE_START,       "repomd",          STATE_REPOMD, 0 },
123   { STATE_START,       "suseinfo",        STATE_SUSEINFO, 0 },  
124   { STATE_SUSEINFO,    "expire",          STATE_EXPIRE, 1 },  
125   { STATE_SUSEINFO,    "products",        STATE_PRODUCTS, 0 },  
126   { STATE_SUSEINFO,    "keywords",        STATE_KEYWORDS, 0 },  
127   { STATE_PRODUCTS,    "id",              STATE_PRODUCT, 1 }, 
128   /* keywords is the suse extension equivalent of
129      tags/content when this one was not yet available.
130      therefore we parse both */ 
131   { STATE_KEYWORDS,    "k",               STATE_KEYWORD, 1 },  
132   /* standard tags */
133   { STATE_REPOMD,      "revision",        STATE_REVISION, 1 },
134   { STATE_REPOMD,      "tags",            STATE_TAGS,  0 },
135   { STATE_REPOMD,      "data",            STATE_DATA,  0 },
136  
137   { STATE_TAGS,        "content",         STATE_CONTENT,  1 },
138   { STATE_TAGS,        "distro",          STATE_DISTRO,  1 },
139
140   { STATE_DATA,        "location",        STATE_LOCATION, 0 },
141   { STATE_DATA,        "checksum",        STATE_CHECKSUM, 1 },  
142   { STATE_DATA,        "timestamp",       STATE_TIMESTAMP, 1 },
143   { STATE_DATA,        "open-checksum",    STATE_OPENCHECKSUM, 1 },
144   { NUMSTATES }
145 };
146
147
148 struct parsedata {
149   int depth;
150   enum state state;
151   int statedepth;
152   char *content;
153   int lcontent;
154   int acontent;
155   int docontent;
156   Pool *pool;
157   Repo *repo;
158   Repodata *data;
159   
160   struct stateswitch *swtab[NUMSTATES];
161   enum state sbtab[NUMSTATES];
162   int timestamp;
163 };
164
165 /*
166  * find attribute
167  */
168
169 static inline const char *
170 find_attr(const char *txt, const char **atts)
171 {
172   for (; *atts; atts += 2)
173     {
174       if (!strcmp(*atts, txt))
175         return atts[1];
176     }
177   return 0;
178 }
179
180
181 static void XMLCALL
182 startElement(void *userData, const char *name, const char **atts)
183 {
184   struct parsedata *pd = userData;
185   /*Pool *pool = pd->pool;*/
186   struct stateswitch *sw;
187
188 #if 0
189   fprintf(stderr, "start: [%d]%s\n", pd->state, name);
190 #endif
191   if (pd->depth != pd->statedepth)
192     {
193       pd->depth++;
194       return;
195     }
196
197   pd->depth++;
198   if (!pd->swtab[pd->state])
199     return;
200   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
201     if (!strcmp(sw->ename, name))
202       break;
203   
204   if (sw->from != pd->state)
205     {
206 #if 1
207       fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
208       exit( 1 );
209 #endif
210       return;
211     }
212   pd->state = sw->to;
213   pd->docontent = sw->docontent;
214   pd->statedepth = pd->depth;
215   pd->lcontent = 0;
216   *pd->content = 0;
217
218   switch(pd->state)
219     {
220     case STATE_START: break;
221     case STATE_REPOMD:
222       {
223         const char *updstr;
224
225         /* this should be OBSOLETE soon */
226         updstr = find_attr("updates", atts);
227         if (updstr)
228           {
229             char *value = strdup(updstr);
230             char *fvalue = value; /* save the first */
231             while (value)
232               {
233                 char *p = strchr(value, ',');
234                 if (*p)
235                   *p++ = 0;
236                 if (*value)
237                   repo_add_poolstr_array(pd->repo, SOLVID_META, REPOSITORY_UPDATES, value);
238                 value = p;
239               }
240             free(fvalue);
241           }
242           break;
243         }
244     case STATE_SUSEINFO: break;
245     case STATE_EXPIRE: break;
246     case STATE_PRODUCTS: break;
247     case STATE_PRODUCT: break;
248     case STATE_KEYWORDS: break;
249     case STATE_KEYWORD: break;
250     case STATE_CONTENT: break;
251     case STATE_REVISION: break;
252     case STATE_DISTRO: break;
253     case STATE_DATA: break;
254     case STATE_LOCATION: break;
255     case STATE_CHECKSUM: break;
256     case STATE_TIMESTAMP: break;
257     case STATE_OPENCHECKSUM: break;
258     case NUMSTATES: break;
259     default: break;
260     }
261   return;
262 }
263
264 static void XMLCALL
265 endElement(void *userData, const char *name)
266 {
267   struct parsedata *pd = userData;
268   /* Pool *pool = pd->pool; */
269
270 #if 0
271       fprintf(stderr, "end: %s\n", name);
272 #endif
273   if (pd->depth != pd->statedepth)
274     {
275       pd->depth--;
276 #if 1
277       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
278 #endif
279       return;
280     }
281
282   pd->depth--;
283   pd->statedepth--;
284   switch (pd->state)
285     {
286     case STATE_START: break;
287     case STATE_REPOMD: 
288       if (pd->timestamp > 0)
289         repodata_set_num(pd->data, SOLVID_META, REPOSITORY_TIMESTAMP, pd->timestamp);
290       break;
291     case STATE_DATA: break;
292     case STATE_LOCATION: break;
293     case STATE_CHECKSUM: break;
294     case STATE_OPENCHECKSUM: break;
295     case STATE_TIMESTAMP:
296       {
297         /**
298          * we want to look for the newest timestamp
299          * of all resources to save it as the time
300          * the metadata was generated
301          */
302         int timestamp = atoi(pd->content);
303         if (timestamp > pd->timestamp)
304           pd->timestamp = timestamp;
305         break;
306       }
307     case STATE_EXPIRE:
308       {
309         int expire = atoi(pd->content);
310         if (expire > 0)
311           repodata_set_num(pd->data, SOLVID_META, REPOSITORY_EXPIRE, expire);
312         break;
313       }
314     case STATE_PRODUCT:
315       if (pd->content)
316         repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_PRODUCTS, pd->content);
317       break;
318       /* repomd.xml content and suseinfo.xml keywords are equivalent */
319     case STATE_CONTENT:
320     case STATE_KEYWORD:
321       if (pd->content)
322         repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_KEYWORDS, pd->content);
323       break;
324     case STATE_REVISION:
325       if (pd->content)
326         repodata_add_poolstr_array(pd->data, SOLVID_META, REPOSITORY_REVISION, pd->content);
327       break;
328     case STATE_DISTRO: break;
329     case STATE_SUSEINFO: break;
330     case STATE_PRODUCTS: break;
331     case STATE_KEYWORDS: break;
332     case NUMSTATES: break;              
333     default:
334       break;
335     }
336
337   pd->state = pd->sbtab[pd->state];
338   pd->docontent = 0;
339   
340   return;
341 }
342
343
344 static void XMLCALL
345 characterData(void *userData, const XML_Char *s, int len)
346 {
347   struct parsedata *pd = userData;
348   int l;
349   char *c;
350   if (!pd->docontent)
351     return;
352   l = pd->lcontent + len + 1;
353   if (l > pd->acontent)
354     {
355       pd->content = realloc(pd->content, l + 256);
356       pd->acontent = l + 256;
357     }
358   c = pd->content + pd->lcontent;
359   pd->lcontent += len;
360   while (len-- > 0)
361     *c++ = *s++;
362   *c = 0;
363 }
364
365 #define BUFF_SIZE 8192
366
367 void
368 repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
369 {
370   Pool *pool = repo->pool;
371   struct parsedata pd;
372   Repodata *data;
373   char buf[BUFF_SIZE];
374   int i, l;
375   struct stateswitch *sw;
376
377   if (!(flags & REPO_REUSE_REPODATA))
378     data = repo_add_repodata(repo, 0);
379   else
380     data = repo_last_repodata(repo);
381
382   memset(&pd, 0, sizeof(pd));
383   pd.timestamp = 0;
384   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
385     {
386       if (!pd.swtab[sw->from])
387         pd.swtab[sw->from] = sw;
388       pd.sbtab[sw->to] = sw->from;
389     }
390   pd.pool = pool;
391   pd.repo = repo;
392   pd.data = data;
393
394   pd.content = malloc(256);
395   pd.acontent = 256;
396   pd.lcontent = 0;
397   XML_Parser parser = XML_ParserCreate(NULL);
398   XML_SetUserData(parser, &pd);
399   XML_SetElementHandler(parser, startElement, endElement);
400   XML_SetCharacterDataHandler(parser, characterData);
401   for (;;)
402     {
403       l = fread(buf, 1, sizeof(buf), fp);
404       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
405         {
406           fprintf(stderr, "repo_repomdxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
407           exit(1);
408         }
409       if (l == 0)
410         break;
411     }
412   XML_ParserFree(parser);
413
414   if (!(flags & REPO_NO_INTERNALIZE))
415     repodata_internalize(data);
416
417   free(pd.content);
418 }
419
420 /* EOF */