#include #include #include "xmlDocumentCache.h" #include #include #include #include #include #include "exceptions.h" #include "curlHelper.h" #include "safeMapFind.h" XmlDocumentCache::Documents XmlDocumentCache::documents; XmlDocumentCache::Queued XmlDocumentCache::queued; AdHoc::Net::CurlMultiHandle XmlDocumentCache::cbf; SimpleMessageException(XmlParseError); SimpleMessageException(DownloadFailed); template static XmlDocumentCache::DocumentPtr helperThrow(const std::string & msg) { throw Exception(msg); } static XmlDocumentCache::DocumentPtr helperReturnDom(XmlDocumentCache::DomParserPtr dp) { return dp->get_document(); } static XmlDocumentCache::DocumentPtr helperReturnDocument(XmlDocumentCache::DocumentPtr dp) { return dp; } XmlDocumentCache::DocumentPtr XmlDocumentCache::getDocument(const Glib::ustring & url, boost::optional encoding, bool html, bool warnings, ExecContext * ec) const { Documents::const_iterator i = documents.find(url); if (i == documents.end()) { queue(url, encoding, html, warnings, ec); cbf.performAll(); queued.clear(); } return AdHoc::safeMapLookup(documents, url)(); } int xmlReadFunc(void * context, char * buffer, int len) { try { std::istream * strm = static_cast(context); strm->read(buffer, len); return strm->gcount(); } catch (const AdHoc::Net::CurlException & error) { return -1; } } int xmlCloseFunc(void *) { return 0; } void XmlDocumentCache::queue(const Glib::ustring & url, boost::optional encoding, bool html, bool warnings, ExecContext *) const { if (queued.find(url) == queued.end()) { cbf.addCurl(url, [url, encoding, html, warnings](std::istream & strm) { if (html) { int flags = warnings ? 0 : XML_PARSE_NOWARNING | XML_PARSE_NOERROR; htmlDocPtr doc = htmlReadIO(xmlReadFunc, xmlCloseFunc, &strm, url.c_str(), encoding ? encoding->c_str() : nullptr, flags); if (!doc) { Logger()->messagebf(LOG_DEBUG, "Download of '%s' failed with error '%s'", url, xmlGetLastError()->message); XmlDocumentCache::documents.insert(XmlDocumentCache::Documents::value_type(url, boost::bind(helperThrow, std::string(xmlGetLastError()->message)))); return; } // Dirty hack alert // xmlpp doesn't play nicely with HTML documents... // sooo ummm, lie and hope it doesn't break something else doc->type = XML_DOCUMENT_NODE; // end hack XmlDocumentCache::documents.insert(XmlDocumentCache::Documents::value_type(url, boost::bind(helperReturnDocument, XmlDocumentCache::DocumentPtr(new xmlpp::Document(doc))))); } else { try { DomParserPtr doc = DomParserPtr(new xmlpp::DomParser()); doc->parse_stream(strm); XmlDocumentCache::documents.insert(XmlDocumentCache::Documents::value_type(url, boost::bind(helperReturnDom, doc))); } catch (const AdHoc::Net::CurlException & error) { Logger()->messagebf(LOG_DEBUG, "Download of '%s' failed with error '%s'", url, error.message); XmlDocumentCache::documents.insert(XmlDocumentCache::Documents::value_type(url, boost::bind(helperThrow, error.message))); } } })->setopt(CURLOPT_ENCODING, "deflate, gzip"); queued.insert(url); } } class XmlDocumentCacheClearer : public LifeCycle { public: typedef bool KeyType; void onIteration() override { Logger()->messagef(LOG_DEBUG, "%s: Clearing XML document cache", __PRETTY_FUNCTION__); XmlDocumentCache::documents.clear(); Logger()->messagef(LOG_DEBUG, "%s: Cleared XML document cache", __PRETTY_FUNCTION__); } }; NAMEDPLUGIN("XmlDocumentCacheClearer", XmlDocumentCacheClearer, LifeCycle);