aria2/src/Xml2MetalinkProcessor.cc

243 lines
7.7 KiB
C++

/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2006 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#include "Xml2MetalinkProcessor.h"
#include "DlAbortEx.h"
#include "Util.h"
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
Xml2MetalinkProcessor::Xml2MetalinkProcessor():doc(0), context(0) {}
Xml2MetalinkProcessor::~Xml2MetalinkProcessor() {
release();
}
void Xml2MetalinkProcessor::release() {
if(context) {
xmlXPathFreeContext(context);
context = 0;
}
if(doc) {
xmlFreeDoc(doc);
doc = 0;
}
}
MetalinkerHandle Xml2MetalinkProcessor::parseFile(const string& filename) {
release();
doc = xmlParseFile(filename.c_str());
if(!doc) {
throw new DlAbortEx("Cannot parse metalink file %s", filename.c_str());
}
context = xmlXPathNewContext(doc);
if(!context) {
throw new DlAbortEx("Cannot create new xpath context");
}
string defaultNamespace = "http://www.metalinker.org/";
if(xmlXPathRegisterNs(context, (xmlChar*)"m",
(xmlChar*)defaultNamespace.c_str()) != 0) {
throw new DlAbortEx("Cannot register namespace %s",
defaultNamespace.c_str());
}
string xpath = "/m:metalink/m:files/m:file";
MetalinkerHandle metalinker(new Metalinker());
for(int index = 1; 1; index++) {
MetalinkEntryHandle entry = getEntry(xpath+"["+Util::itos(index)+"]");
if(!entry.get()) {
break;
} else {
metalinker->entries.push_back(entry);
}
}
return metalinker;
}
MetalinkEntryHandle Xml2MetalinkProcessor::getEntry(const string& xpath) {
xmlXPathObjectPtr result = xpathEvaluation(xpath);
if(!result) {
return 0;
}
xmlNodeSetPtr nodeSet = result->nodesetval;
xmlNodePtr node = nodeSet->nodeTab[0];
string filename = Util::trim(xmlAttribute(node, "name"));
xmlXPathFreeObject(result);
MetalinkEntryHandle entry(new MetalinkEntry());
entry->filename = filename;
entry->size = STRTOLL(xpathContent(xpath+"/m:size").c_str());
entry->version = Util::trim(xpathContent(xpath+"/m:version"));
entry->language = Util::trim(xpathContent(xpath+"/m:language"));
entry->os = Util::trim(xpathContent(xpath+"/m:os"));
#ifdef ENABLE_MESSAGE_DIGEST
string md;
md = Util::toLower(Util::trim(xpathContent(xpath+"/m:verification/m:hash[@type=\"sha1\"]")));
if(md.size() > 0) {
entry->checksum.setMessageDigest(md);
entry->checksum.setDigestAlgo(DIGEST_ALGO_SHA1);
} else {
md = Util::toLower(Util::trim(xpathContent(xpath+"/m:verification/m:hash[@type=\"md5\"]")));
if(md.size() > 0) {
entry->checksum.setMessageDigest(md);
entry->checksum.setDigestAlgo(DIGEST_ALGO_MD5);
}
}
entry->chunkChecksum = getPieceHash(xpath+"/m:verification/m:pieces[@type=\"sha1\"]", entry->size);
#endif // ENABLE_MESSAGE_DIGEST
for(int index = 1; 1; index++) {
MetalinkResourceHandle resource(getResource(xpath+"/m:resources/m:url["+Util::itos(index)+"]"));
if(!resource.get()) {
break;
} else {
entry->resources.push_back(resource);
}
}
return entry;
}
MetalinkChunkChecksumHandle Xml2MetalinkProcessor::getPieceHash(const string& xpath,
uint64_t totalSize)
{
MetalinkChunkChecksumHandle chunkChecksum = new MetalinkChunkChecksum();
chunkChecksum->digestAlgo = DIGEST_ALGO_SHA1;
xmlXPathObjectPtr result = xpathEvaluation(xpath);
if(!result) {
return 0;
}
xmlNodeSetPtr nodeSet = result->nodesetval;
xmlNodePtr node = nodeSet->nodeTab[0];
chunkChecksum->pieceLength = STRTOLL(Util::trim(xmlAttribute(node, "length")).c_str());
xmlXPathFreeObject(result);
uint64_t numPiece =
(totalSize+chunkChecksum->pieceLength-1)/chunkChecksum->pieceLength;
for(uint64_t i = 0; i < numPiece; i++) {
string pieceHash = Util::trim(xpathContent(xpath+"/m:hash[@piece=\""+Util::ullitos(i)+"\"]"));
if(pieceHash == "") {
throw new DlAbortEx("Piece hash missing. index=%u", i);
}
chunkChecksum->pieceHashes.push_back(pieceHash);
}
return chunkChecksum;
}
MetalinkResourceHandle Xml2MetalinkProcessor::getResource(const string& xpath) {
xmlXPathObjectPtr result = xpathEvaluation(xpath);
if(!result) {
return 0;
}
MetalinkResourceHandle resource(new MetalinkResource());
xmlNodeSetPtr nodeSet = result->nodesetval;
xmlNodePtr node = nodeSet->nodeTab[0];
string type = Util::trim(xmlAttribute(node, "type"));
if(type == "ftp") {
resource->type = MetalinkResource::TYPE_FTP;
} else if(type == "http") {
resource->type = MetalinkResource::TYPE_HTTP;
} else if(type == "https") {
resource->type = MetalinkResource::TYPE_HTTPS;
} else if(type == "bittorrent") {
resource->type = MetalinkResource::TYPE_BITTORRENT;
} else {
resource->type = MetalinkResource::TYPE_NOT_SUPPORTED;
}
string pref = Util::trim(xmlAttribute(node, "preference"));
if(pref.empty()) {
resource->preference = 100;
} else {
resource->preference = STRTOLL(pref.c_str());
}
resource->location = Util::trim(xmlAttribute(node, "location"));
resource->url = Util::trim(xmlContent(node));
xmlXPathFreeObject(result);
return resource;
}
xmlXPathObjectPtr Xml2MetalinkProcessor::xpathEvaluation(const string& xpath) {
xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)xpath.c_str(),
context);
if(!result) {
throw new DlAbortEx("Cannot evaluate xpath %s", xpath.c_str());
}
if(xmlXPathNodeSetIsEmpty(result->nodesetval)) {
xmlXPathFreeObject(result);
return 0;
}
return result;
}
string Xml2MetalinkProcessor::xmlAttribute(xmlNodePtr node, const string& attrName) {
xmlChar* temp = xmlGetNoNsProp(node, (xmlChar*)attrName.c_str());
if(!temp) {
return "";
} else {
string attr = (char*)temp;
xmlFree(temp);
return attr;
}
}
string Xml2MetalinkProcessor::xmlContent(xmlNodePtr node) {
xmlChar* temp = xmlNodeGetContent(node);
if(!temp) {
return "";
} else {
string content = (char*)temp;
xmlFree(temp);
return content;
}
}
string Xml2MetalinkProcessor::xpathContent(const string& xpath) {
xmlXPathObjectPtr result = xpathEvaluation(xpath);
if(!result) {
return "";
}
xmlNodeSetPtr nodeSet = result->nodesetval;
xmlNodePtr node = nodeSet->nodeTab[0]->children;
string content = (char*)node->content;
xmlXPathFreeObject(result);
return content;
}