+/*\r
+ * Copyright 2010 Internet2\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * XMLProtocolProvider.cpp\r
+ *\r
+ * XML-based protocol provider.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "exceptions.h"\r
+#include "binding/ProtocolProvider.h"\r
+#include "util/DOMPropertySet.h"\r
+#include "util/SPConstants.h"\r
+\r
+#include <map>\r
+#include <xmltooling/io/HTTPResponse.h>\r
+#include <xmltooling/util/NDC.h>\r
+#include <xmltooling/util/ReloadableXMLFile.h>\r
+#include <xmltooling/util/Threads.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using shibspconstants::SHIB2SPPROTOCOLS_NS;\r
+using namespace shibsp;\r
+using namespace xmltooling;\r
+using namespace std;\r
+\r
+namespace shibsp {\r
+\r
+ static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);\r
+ static const XMLCh Binding[] = UNICODE_LITERAL_7(B,i,n,d,i,n,g);\r
+ static const XMLCh Protocol[] = UNICODE_LITERAL_8(P,r,o,t,o,c,o,l);\r
+ static const XMLCh Protocols[] = UNICODE_LITERAL_9(P,r,o,t,o,c,o,l,s);\r
+ static const XMLCh Service[] = UNICODE_LITERAL_7(S,e,r,v,i,c,e);\r
+\r
+#if defined (_MSC_VER)\r
+ #pragma warning( push )\r
+ #pragma warning( disable : 4250 )\r
+#endif\r
+\r
+ class SHIBSP_DLLLOCAL XMLProtocolProviderImpl : public DOMNodeFilter, DOMPropertySet\r
+ {\r
+ public:\r
+ XMLProtocolProviderImpl(const DOMElement* e, Category& log);\r
+ ~XMLProtocolProviderImpl() {\r
+ for (protmap_t::iterator i = m_map.begin(); i != m_map.end(); ++i) {\r
+ delete i->second.first;\r
+ for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<PropertySet>());\r
+ }\r
+ if (m_document)\r
+ m_document->release();\r
+ }\r
+\r
+ void setDocument(DOMDocument* doc) {\r
+ m_document = doc;\r
+ }\r
+\r
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
+ short\r
+#else\r
+ FilterAction\r
+#endif\r
+ acceptNode(const DOMNode* node) const {\r
+ return FILTER_REJECT;\r
+ }\r
+\r
+ private:\r
+ DOMDocument* m_document;\r
+ // Map of protocol/service pair to a service propset plus an array of Binding propsets.\r
+ typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
+ protmap_t m_map;\r
+\r
+ friend class SHIBSP_DLLLOCAL XMLProtocolProvider;\r
+ };\r
+\r
+ class XMLProtocolProvider : public ProtocolProvider, public ReloadableXMLFile\r
+ {\r
+ public:\r
+ XMLProtocolProvider(const DOMElement* e)\r
+ : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".ProtocolProvider.XML")), m_impl(nullptr) {\r
+ background_load(); // guarantees an exception or the policy is loaded\r
+ }\r
+\r
+ ~XMLProtocolProvider() {\r
+ shutdown();\r
+ delete m_impl;\r
+ }\r
+\r
+ const PropertySet* getService(const char* protocol, const char* service) const {\r
+ XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
+ return (i != m_impl->m_map.end()) ? i->second.first : nullptr;\r
+ }\r
+\r
+ const vector<const PropertySet*>& getBindings(const char* protocol, const char* service) const {\r
+ XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
+ if (i != m_impl->m_map.end())\r
+ return i->second.second;\r
+ throw ConfigurationException("ProtocolProvider can't return bindings for undefined protocol and service.");\r
+ }\r
+\r
+ protected:\r
+ pair<bool,DOMElement*> load(bool backup);\r
+ pair<bool,DOMElement*> background_load();\r
+\r
+ private:\r
+ XMLProtocolProviderImpl* m_impl;\r
+ };\r
+\r
+#if defined (_MSC_VER)\r
+ #pragma warning( pop )\r
+#endif\r
+\r
+ ProtocolProvider* SHIBSP_DLLLOCAL XMLProtocolProviderFactory(const DOMElement* const & e)\r
+ {\r
+ return new XMLProtocolProvider(e);\r
+ }\r
+}\r
+\r
+void SHIBSP_API shibsp::registerProtocolProviders()\r
+{\r
+ SPConfig::getConfig().ProtocolProviderManager.registerFactory(XML_PROTOCOL_PROVIDER, XMLProtocolProviderFactory);\r
+}\r
+\r
+ProtocolProvider::ProtocolProvider()\r
+{\r
+}\r
+\r
+ProtocolProvider::~ProtocolProvider()\r
+{\r
+}\r
+\r
+XMLProtocolProviderImpl::XMLProtocolProviderImpl(const DOMElement* e, Category& log) : m_document(nullptr)\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("XMLProtocolProviderImpl");\r
+#endif\r
+ //typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
+\r
+ if (!XMLHelper::isNodeNamed(e, SHIB2SPPROTOCOLS_NS, Protocols))\r
+ throw ConfigurationException("XML ProtocolProvider requires prot:Protocols at root of configuration.");\r
+\r
+ e = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
+ while (e) {\r
+ string id = XMLHelper::getAttrString(e, nullptr, _id);\r
+ if (!id.empty()) {\r
+ const DOMElement* svc = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Service);\r
+ while (svc) {\r
+ string svcid = XMLHelper::getAttrString(svc, nullptr, _id);\r
+ if (!svcid.empty()) {\r
+ pair< PropertySet*,vector<const PropertySet*> >& entry = m_map[make_pair(id,svcid)];\r
+ if (!entry.first) {\r
+ // Wrap the Service in a propset.\r
+ DOMPropertySet* svcprop = new DOMPropertySet();\r
+ entry.first = svcprop;\r
+ svcprop->load(svc, &log, this);\r
+\r
+ // Walk the Bindings.\r
+ const DOMElement* bind = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Binding);\r
+ while (bind) {\r
+ DOMPropertySet* bindprop = new DOMPropertySet();\r
+ entry.second.push_back(bindprop);\r
+ bindprop->load(bind, &log, this);\r
+ bind = XMLHelper::getNextSiblingElement(bind, SHIB2SPPROTOCOLS_NS, Binding);\r
+ }\r
+ }\r
+ }\r
+ svc = XMLHelper::getNextSiblingElement(svc, SHIB2SPPROTOCOLS_NS, Service);\r
+ }\r
+ }\r
+ e = XMLHelper::getNextSiblingElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
+ }\r
+}\r
+\r
+\r
+pair<bool,DOMElement*> XMLProtocolProvider::load(bool backup)\r
+{\r
+ // Load from source using base class.\r
+ pair<bool,DOMElement*> raw = ReloadableXMLFile::load(backup);\r
+\r
+ // If we own it, wrap it.\r
+ XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);\r
+\r
+ XMLProtocolProviderImpl* impl = new XMLProtocolProviderImpl(raw.second, m_log);\r
+\r
+ // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
+ impl->setDocument(docjanitor.release());\r
+\r
+ // Perform the swap inside a lock.\r
+ if (m_lock)\r
+ m_lock->wrlock();\r
+ SharedLock locker(m_lock, false);\r
+ delete m_impl;\r
+ m_impl = impl;\r
+\r
+\r
+ return make_pair(false,(DOMElement*)nullptr);\r
+}\r
+\r
+pair<bool,DOMElement*> XMLProtocolProvider::background_load()\r
+{\r
+ try {\r
+ return load(false);\r
+ }\r
+ catch (long& ex) {\r
+ if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)\r
+ m_log.info("remote resource (%s) unchanged", m_source.c_str());\r
+ if (!m_loaded && !m_backing.empty())\r
+ return load(true);\r
+ throw;\r
+ }\r
+ catch (exception&) {\r
+ if (!m_loaded && !m_backing.empty())\r
+ return load(true);\r
+ throw;\r
+ }\r
+}\r