#include "apiImpl.h" #include #include #include #include #include #include #include namespace MirrorSearch { SearchImpl::SearchImpl(IceTray::DatabasePoolPtr db) : IceTray::AbstractDatabaseClient(db), log(LOGMANAGER()->getLogger()) { } SearchServices SearchImpl::getServices(const ::Ice::Current&) { return fetch(sql::getServices); } typedef std::shared_ptr xmlDocSPtr; typedef std::shared_ptr xmlXPathContextSPtr; typedef std::shared_ptr xmlXPathObjectSPtr; static auto getDoc(const ::std::string & url, int flags) { if (auto doc = xmlDocSPtr(htmlReadFile(url.c_str(), NULL, flags), xmlFreeDoc)) { return doc; } throw XmlError("Failed to open " + url); } static auto getXPathCxt(const xmlDocSPtr & doc) { if (auto xpathCtx = xmlXPathContextSPtr(xmlXPathNewContext(doc.get()), xmlXPathFreeContext)) { return xpathCtx; } throw XmlError("Failed to create xpath context"); } static auto getXPathObj(const ::std::string & xpath, const xmlXPathContextSPtr & ctx, xmlXPathObjectType type) { if (auto xpathObj = xmlXPathObjectSPtr(xmlXPathEvalExpression(BAD_CAST xpath.c_str(), ctx.get()), xmlXPathFreeObject)) { if (xpathObj->type != type) { throw XmlError("Xpath evaluates to wrong type " + xpath); } return xpathObj; } throw XmlError("Failed to evaluate xpath " + xpath); } void SearchImpl::callService(const ::std::string & fn, const SearchServicePtr & s, SearchHits & sh) const { auto fmt = AdHoc::Buffer::getFormat(s->baseurl); auto url = (*fmt % fn).str(); auto doc = getDoc(url, HTML_PARSE_RECOVER | HTML_PARSE_NODEFDTD | HTML_PARSE_NOIMPLIED | HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); auto xpathCtx = getXPathCxt(doc); auto xpathObj = getXPathObj(s->listxpath, xpathCtx, xmlXPathObjectType::XPATH_NODESET); log->messagebf(LOG::INFO, "%d nodes matched %s", xpathObj->nodesetval->nodeNr, s->listxpath); for (int row = 0; row < xpathObj->nodesetval->nodeNr; row += 1) { xpathCtx->node = xpathObj->nodesetval->nodeTab[row]; auto xpathObjI = getXPathObj(s->urlxpath, xpathCtx, xmlXPathObjectType::XPATH_STRING); if (xpathObjI->stringval && *xpathObjI->stringval) { sh.push_back(new SearchHit(0, s->id, (const char *) xpathObjI->stringval)); } } } SearchHits SearchImpl::getMatches(const ::std::string & fn, const ::Ice::Current & c) { SearchHits sh; for (const auto & s : getServices(c)) { callService(fn, s, sh); } return sh; } ::IceUtil::Optional<::std::string> SearchImpl::feelingLucky(const ::std::string & fn, const ::Ice::Current & c) { const auto ms = getMatches(fn, c); if (ms.empty()) return IceUtil::None; return ms.front()->url; } }