1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MetaLinkParser.cc
13 #include "zypp/media/MetaLinkParser.h"
14 #include "zypp/base/Logger.h"
16 #include <sys/types.h>
26 #include <libxml2/libxml/SAX2.h>
29 using namespace zypp::base;
62 static struct stateswitch stateswitches[] = {
63 { STATE_START, "metalink", STATE_METALINK, 0 },
64 { STATE_METALINK, "files", STATE_FILES, 0 },
65 { STATE_METALINK, "file", STATE_M4FILE, 0 },
66 { STATE_FILES, "file", STATE_FILE, 0 },
67 { STATE_FILE, "size", STATE_SIZE, 1 },
68 { STATE_FILE, "verification", STATE_VERIFICATION, 0 },
69 { STATE_FILE, "resources", STATE_RESOURCES, 0 },
70 { STATE_VERIFICATION, "hash", STATE_HASH, 1 },
71 { STATE_VERIFICATION, "pieces", STATE_PIECES, 0 },
72 { STATE_PIECES, "hash", STATE_PHASH, 1 },
73 { STATE_RESOURCES, "url", STATE_URL, 1 },
74 { STATE_M4FILE, "size", STATE_M4SIZE, 1 },
75 { STATE_M4FILE, "hash", STATE_M4HASH, 1},
76 { STATE_M4FILE, "url", STATE_M4URL, 1},
77 { STATE_M4FILE, "pieces", STATE_M4PIECES, 0},
78 { STATE_M4PIECES, "hash", STATE_M4PHASH, 1 },
90 static void XMLCALL startElement(void *userData, const xmlChar *name, const xmlChar **atts);
91 static void XMLCALL endElement(void *userData, const xmlChar *name);
92 static void XMLCALL characterData(void *userData, const xmlChar *s, int len);
94 struct ml_parsedata : private zypp::base::NonCopyable {
98 , state( STATE_START )
100 , content( reinterpret_cast<char *>(malloc(256)) )
115 struct stateswitch *sw;
117 memset( swtab, 0, sizeof(swtab) );
118 memset( sbtab, 0, sizeof(sbtab) );
119 for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
121 if (!swtab[sw->from])
122 swtab[sw->from] = sw;
123 sbtab[sw->to] = sw->from;
127 memset(&sax, 0, sizeof(sax));
128 sax.startElement = startElement;
129 sax.endElement = endElement;
130 sax.characters = characterData;
132 //internally creates a copy of xmlSaxHandler, so having it as local variable is save
133 parser = xmlCreatePushParserCtxt(&sax, this, NULL, 0, NULL);
139 xmlFreeParserCtxt(parser);
145 xmlParserCtxtPtr parser;
153 struct stateswitch *swtab[NUMSTATES];
154 enum state sbtab[NUMSTATES];
159 vector<struct ml_url> urls;
163 vector<unsigned char> piece;
167 vector<unsigned char> sha1;
169 vector<unsigned char> zsync;
172 vector<unsigned char> chksum;
177 find_attr(const char *txt, const xmlChar **atts)
183 for (; *atts; atts += 2)
185 if (!strcmp(reinterpret_cast<const char*>(*atts), txt))
186 return reinterpret_cast<const char*>(atts[1]);
192 startElement(void *userData, const xmlChar *name, const xmlChar **atts)
194 struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
195 struct stateswitch *sw;
196 if (pd->depth != pd->statedepth)
202 if (!pd->swtab[pd->state])
204 for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) /* find name in statetable */
205 if (sw->ename == reinterpret_cast<const char *>(name))
207 if (sw->from != pd->state)
209 if ((sw->to == STATE_FILE || sw->to == STATE_M4FILE) && pd->gotfile++)
210 return; /* ignore all but the first file */
211 //printf("start depth %d name %s\n", pd->depth, name);
213 pd->docontent = sw->docontent;
214 pd->statedepth = pd->depth;
222 const char *priority = find_attr("priority", atts);
223 const char *preference = find_attr("preference", atts);
225 pd->urls.push_back(ml_url());
227 prio = atoi(priority);
229 prio = 101 - atoi(preference);
232 pd->urls.back().priority = prio;
238 const char *type = find_attr("type", atts);
239 const char *length = find_attr("length", atts);
242 if (!type || !length)
244 pd->state = pd->sbtab[pd->state];
248 blksize = strtoul(length, 0, 10);
249 if (!blksize || (pd->blksize && pd->blksize != blksize))
251 pd->state = pd->sbtab[pd->state];
255 pd->blksize = blksize;
258 if (!strcmp(type, "sha1") || !strcmp(type, "sha-1"))
260 else if (!strcmp(type, "zsync"))
264 pd->state = pd->sbtab[pd->state];
273 const char *type = find_attr("type", atts);
276 if ((!strcmp(type, "sha1") || !strcmp(type, "sha-1")) && pd->chksuml < 20)
278 else if (!strcmp(type, "sha256") || !strcmp(type, "sha-256"))
282 pd->state = pd->sbtab[pd->state];
291 const char *piece = find_attr("piece", atts);
292 if (pd->state == STATE_PHASH && (!piece || atoi(piece) != pd->npiece))
294 pd->state = pd->sbtab[pd->state];
305 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
308 for (i = 0; i < buflen; i++)
310 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0') \
311 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10)) \
312 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10)) \
323 buf[i] = (buf[i] << 4) | v;
330 endElement(void *userData, const xmlChar *name)
332 struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
333 // printf("end depth %d-%d name %s\n", pd->depth, pd->statedepth, name);
334 if (pd->depth != pd->statedepth)
345 pd->size = (off_t)strtoull(pd->content, 0, 10);
350 pd->chksum.resize(pd->chksuml, 0);
351 if (strlen(pd->content) != size_t(pd->chksuml) * 2 || !hexstr2bytes(&pd->chksum[0], pd->content, pd->chksuml))
359 if (strlen(pd->content) != size_t(pd->piecel) * 2)
361 pd->piece.resize(pd->piecel * (pd->npiece + 1), 0);
362 if (!hexstr2bytes(&pd->piece[pd->piecel * pd->npiece], pd->content, pd->piecel))
364 pd->piece.resize(pd->piecel * pd->npiece, 0);
373 pd->zsync = pd->piece;
374 pd->nzsync = pd->npiece;
378 pd->sha1 = pd->piece;
379 pd->nsha1 = pd->npiece;
381 pd->piecel = pd->npiece = 0;
388 pd->urls[pd->nurls].url = string(pd->content);
395 pd->state = pd->sbtab[pd->state];
400 characterData(void *userData, const xmlChar *s, int len)
402 struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
407 l = pd->lcontent + len + 1;
408 if (l > pd->acontent)
410 pd->content = reinterpret_cast<char *>(realloc(pd->content, l + 256));
411 pd->acontent = l + 256;
413 c = pd->content + pd->lcontent;
421 MetaLinkParser::MetaLinkParser()
422 : pd( new ml_parsedata )
425 MetaLinkParser::~MetaLinkParser()
431 MetaLinkParser::parse(const Pathname &filename)
433 parse(InputStream(filename));
437 MetaLinkParser::parse(const InputStream &is)
441 ZYPP_THROW(Exception("MetaLinkParser: no such file"));
442 while (is.stream().good())
444 is.stream().read(buf, sizeof(buf));
445 parseBytes(buf, is.stream().gcount());
451 MetaLinkParser::parseBytes(const char *buf, size_t len)
456 if (xmlParseChunk(pd->parser, buf, len, 0)) {
457 ZYPP_THROW(Exception("Parse Error"));
461 static bool urlcmp(const ml_url &a, const ml_url &b)
463 return a.priority < b.priority;
467 MetaLinkParser::parseEnd()
469 if (xmlParseChunk(pd->parser, NULL, 0, 1)) {
470 ZYPP_THROW(Exception("Parse Error"));
473 stable_sort(pd->urls.begin(), pd->urls.end(), urlcmp);
477 MetaLinkParser::getUrls()
479 std::vector<Url> urls;
481 for (i = 0; i < pd->nurls; ++i)
482 urls.push_back(Url(pd->urls[i].url));
487 MetaLinkParser::getBlockList()
490 MediaBlockList bl(pd->size);
491 if (pd->chksuml == 20)
492 bl.setFileChecksum("SHA1", pd->chksuml, &pd->chksum[0]);
493 else if (pd->chksuml == 32)
494 bl.setFileChecksum("SHA256", pd->chksuml, &pd->chksum[0]);
495 if (pd->size != off_t(-1) && pd->blksize)
497 size_t nb = (pd->size + pd->blksize - 1) / pd->blksize;
499 size_t size = pd->blksize;
500 for (i = 0; i < nb; i++)
504 size = pd->size % pd->blksize;
508 size_t blkno = bl.addBlock(off, size);
509 if (int(i) < pd->nsha1)
511 bl.setChecksum(blkno, "SHA1", 20, &pd->sha1[20 * i]);
512 if (int(i) < pd->nzsync)
514 unsigned char *p = &pd->zsync[4 * i];
515 bl.setRsum(blkno, 4, p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24, pd->blksize);