/* */ #include "XML2SAXMetalinkProcessor.h" #include #include "BinaryStream.h" #include "MetalinkParserStateMachine.h" #include "Metalinker.h" #include "MetalinkEntry.h" #include "util.h" #include "message.h" #include "DlAbortEx.h" #include "A2STR.h" #include "error_code.h" namespace aria2 { namespace { class SessionData { public: SharedHandle stm_; std::deque charactersStack_; SessionData(const SharedHandle& stm):stm_(stm) {} }; } // namespace namespace { void mlStartElement (void* userData, const xmlChar* srcLocalname, const xmlChar* srcPrefix, const xmlChar* srcNsUri, int numNamespaces, const xmlChar **namespaces, int numAttrs, int numDefaulted, const xmlChar **attrs) { SessionData* sd = reinterpret_cast(userData); std::vector xmlAttrs; size_t index = 0; for(int attrIndex = 0; attrIndex < numAttrs; ++attrIndex, index += 5) { XmlAttr xmlAttr; assert(attrs[index]); xmlAttr.localname = reinterpret_cast(attrs[index]); if(attrs[index+1]) { xmlAttr.prefix = reinterpret_cast(attrs[index+1]); } if(attrs[index+2]) { xmlAttr.nsUri = reinterpret_cast(attrs[index+2]); } const char* valueBegin = reinterpret_cast(attrs[index+3]); const char* valueEnd = reinterpret_cast(attrs[index+4]); xmlAttr.value = std::string(valueBegin, valueEnd); xmlAttrs.push_back(xmlAttr); } assert(srcLocalname); std::string localname = reinterpret_cast(srcLocalname); std::string prefix; std::string nsUri; if(srcPrefix) { prefix = reinterpret_cast(srcPrefix); } if(srcNsUri) { nsUri = reinterpret_cast(srcNsUri); } sd->stm_->beginElement(localname, prefix, nsUri, xmlAttrs); if(sd->stm_->needsCharactersBuffering()) { sd->charactersStack_.push_front(A2STR::NIL); } } } // namespace namespace { void mlEndElement (void* userData, const xmlChar* srcLocalname, const xmlChar* srcPrefix, const xmlChar* srcNsUri) { SessionData* sd = reinterpret_cast(userData); std::string characters; if(sd->stm_->needsCharactersBuffering()) { characters = sd->charactersStack_.front(); sd->charactersStack_.pop_front(); } std::string localname = reinterpret_cast(srcLocalname); std::string prefix; std::string nsUri; if(srcPrefix) { prefix = reinterpret_cast(srcPrefix); } if(srcNsUri) { nsUri = reinterpret_cast(srcNsUri); } sd->stm_->endElement(localname, prefix, nsUri, characters); } } // namespace namespace { void mlCharacters(void* userData, const xmlChar* ch, int len) { SessionData* sd = reinterpret_cast(userData); if(sd->stm_->needsCharactersBuffering()) { sd->charactersStack_.front() += std::string(&ch[0], &ch[len]); } } } // namespace namespace { xmlSAXHandler mySAXHandler = { 0, // internalSubsetSAXFunc 0, // isStandaloneSAXFunc 0, // hasInternalSubsetSAXFunc 0, // hasExternalSubsetSAXFunc 0, // resolveEntitySAXFunc 0, // getEntitySAXFunc 0, // entityDeclSAXFunc 0, // notationDeclSAXFunc 0, // attributeDeclSAXFunc 0, // elementDeclSAXFunc 0, // unparsedEntityDeclSAXFunc 0, // setDocumentLocatorSAXFunc 0, // startDocumentSAXFunc 0, // endDocumentSAXFunc 0, // startElementSAXFunc 0, // endElementSAXFunc 0, // referenceSAXFunc &mlCharacters, // charactersSAXFunc 0, // ignorableWhitespaceSAXFunc 0, // processingInstructionSAXFunc 0, // commentSAXFunc 0, // warningSAXFunc 0, // errorSAXFunc 0, // fatalErrorSAXFunc 0, // getParameterEntitySAXFunc 0, // cdataBlockSAXFunc 0, // externalSubsetSAXFunc XML_SAX2_MAGIC, // unsigned int initialized 0, // void * _private &mlStartElement, // startElementNsSAX2Func &mlEndElement, // endElementNsSAX2Func 0, // xmlStructuredErrorFunc }; } // namespace MetalinkProcessor::MetalinkProcessor() {} MetalinkProcessor::~MetalinkProcessor() {} SharedHandle MetalinkProcessor::parseFile(const std::string& filename) { stm_.reset(new MetalinkParserStateMachine()); SharedHandle sessionData(new SessionData(stm_)); // Old libxml2(at least 2.7.6, Ubuntu 10.04LTS) does not read stdin // when "/dev/stdin" is passed as filename while 2.7.7 does. So we // convert DEV_STDIN to "-" for compatibility. std::string nfilename; if(filename == DEV_STDIN) { nfilename = "-"; } else { nfilename = filename; } int retval = xmlSAXUserParseFile(&mySAXHandler, sessionData.get(), nfilename.c_str()); if(retval != 0) { throw DL_ABORT_EX2(MSG_CANNOT_PARSE_METALINK, error_code::METALINK_PARSE_ERROR); } if(!stm_->finished()) { throw DL_ABORT_EX2(MSG_CANNOT_PARSE_METALINK, error_code::METALINK_PARSE_ERROR); } if(!stm_->getErrors().empty()) { throw DL_ABORT_EX2(stm_->getErrorString(), error_code::METALINK_PARSE_ERROR); } return stm_->getResult(); } SharedHandle MetalinkProcessor::parseFromBinaryStream(const SharedHandle& binaryStream) { stm_.reset(new MetalinkParserStateMachine()); size_t bufSize = 4096; unsigned char buf[bufSize]; ssize_t res = binaryStream->readData(buf, 4, 0); if(res != 4) { throw DL_ABORT_EX2("Too small data for parsing XML.", error_code::METALINK_PARSE_ERROR); } SharedHandle sessionData(new SessionData(stm_)); xmlParserCtxtPtr ctx = xmlCreatePushParserCtxt (&mySAXHandler, sessionData.get(), reinterpret_cast(buf), res, 0); auto_delete deleter(ctx, xmlFreeParserCtxt); off_t readOffset = res; while(1) { ssize_t res = binaryStream->readData(buf, bufSize, readOffset); if(res == 0) { break; } if(xmlParseChunk(ctx, reinterpret_cast(buf), res, 0) != 0) { throw DL_ABORT_EX2(MSG_CANNOT_PARSE_METALINK, error_code::METALINK_PARSE_ERROR); } readOffset += res; } xmlParseChunk(ctx, reinterpret_cast(buf), 0, 1); if(!stm_->finished()) { throw DL_ABORT_EX2(MSG_CANNOT_PARSE_METALINK, error_code::METALINK_PARSE_ERROR); } if(!stm_->getErrors().empty()) { throw DL_ABORT_EX2(stm_->getErrorString(), error_code::METALINK_PARSE_ERROR); } return stm_->getResult(); } } // namespace aria2