SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / binding / impl / XMLProtocolProvider.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * XMLProtocolProvider.cpp
23  *
24  * XML-based protocol provider.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "binding/ProtocolProvider.h"
30 #include "util/DOMPropertySet.h"
31 #include "util/SPConstants.h"
32
33 #include <map>
34 #include <boost/shared_ptr.hpp>
35 #include <xmltooling/io/HTTPResponse.h>
36 #include <xmltooling/util/NDC.h>
37 #include <xmltooling/util/ReloadableXMLFile.h>
38 #include <xmltooling/util/Threads.h>
39 #include <xmltooling/util/XMLHelper.h>
40 #include <xercesc/util/XMLUniDefs.hpp>
41
42 using shibspconstants::SHIB2SPPROTOCOLS_NS;
43 using namespace shibsp;
44 using namespace xmltooling;
45 using namespace boost;
46 using namespace std;
47
48 namespace shibsp {
49     static const XMLCh _id[] =          UNICODE_LITERAL_2(i,d);
50     static const XMLCh Binding[] =      UNICODE_LITERAL_7(B,i,n,d,i,n,g);
51     static const XMLCh Initiator[] =    UNICODE_LITERAL_9(I,n,i,t,i,a,t,o,r);
52     static const XMLCh Protocol[] =     UNICODE_LITERAL_8(P,r,o,t,o,c,o,l);
53     static const XMLCh Protocols[] =    UNICODE_LITERAL_9(P,r,o,t,o,c,o,l,s);
54     static const XMLCh Service[] =      UNICODE_LITERAL_7(S,e,r,v,i,c,e);
55
56 #if defined (_MSC_VER)
57     #pragma warning( push )
58     #pragma warning( disable : 4250 )
59 #endif
60
61     class SHIBSP_DLLLOCAL XMLProtocolProviderImpl : public DOMNodeFilter, DOMPropertySet
62     {
63     public:
64         XMLProtocolProviderImpl(const DOMElement* e, Category& log);
65         ~XMLProtocolProviderImpl() {
66             if (m_document)
67                 m_document->release();
68         }
69
70         void setDocument(DOMDocument* doc) {
71             m_document = doc;
72         }
73
74 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
75         short
76 #else
77         FilterAction
78 #endif
79         acceptNode(const DOMNode* node) const {
80             return FILTER_REJECT;
81         }
82
83     private:
84         DOMDocument* m_document;
85         // Map of protocol/service pair to an Initiator propset plus an array of Binding propsets.
86         typedef map< pair<string,string>, pair< const PropertySet*,vector<const PropertySet*> > > protmap_t;
87         protmap_t m_map;
88         vector< boost::shared_ptr<PropertySet> > m_propsetJanitor;  // needed to maintain vector API
89
90         friend class SHIBSP_DLLLOCAL XMLProtocolProvider;
91     };
92
93     class XMLProtocolProvider : public ProtocolProvider, public ReloadableXMLFile
94     {
95     public:
96         XMLProtocolProvider(const DOMElement* e)
97                 : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".ProtocolProvider.XML")) {
98             background_load(); // guarantees an exception or the policy is loaded
99         }
100
101         ~XMLProtocolProvider() {
102             shutdown();
103         }
104
105         const PropertySet* getInitiator(const char* protocol, const char* service) const {
106             XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));
107             return (i != m_impl->m_map.end()) ? i->second.first : nullptr;
108         }
109
110         const vector<const PropertySet*>& getBindings(const char* protocol, const char* service) const {
111             XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));
112             return (i != m_impl->m_map.end()) ? i->second.second : m_noBindings;
113         }
114
115     protected:
116         pair<bool,DOMElement*> load(bool backup);
117         pair<bool,DOMElement*> background_load();
118
119     private:
120         static vector<const PropertySet*> m_noBindings;
121         scoped_ptr<XMLProtocolProviderImpl> m_impl;
122     };
123
124 #if defined (_MSC_VER)
125     #pragma warning( pop )
126 #endif
127
128     ProtocolProvider* SHIBSP_DLLLOCAL XMLProtocolProviderFactory(const DOMElement* const & e)
129     {
130         return new XMLProtocolProvider(e);
131     }
132 }
133
134 void SHIBSP_API shibsp::registerProtocolProviders()
135 {
136     SPConfig::getConfig().ProtocolProviderManager.registerFactory(XML_PROTOCOL_PROVIDER, XMLProtocolProviderFactory);
137 }
138
139 ProtocolProvider::ProtocolProvider()
140 {
141 }
142
143 ProtocolProvider::~ProtocolProvider()
144 {
145 }
146
147 vector<const PropertySet*> XMLProtocolProvider::m_noBindings;
148
149 XMLProtocolProviderImpl::XMLProtocolProviderImpl(const DOMElement* e, Category& log) : m_document(nullptr)
150 {
151 #ifdef _DEBUG
152     xmltooling::NDC ndc("XMLProtocolProviderImpl");
153 #endif
154     if (!XMLHelper::isNodeNamed(e, SHIB2SPPROTOCOLS_NS, Protocols))
155         throw ConfigurationException("XML ProtocolProvider requires prot:Protocols at root of configuration.");
156
157     e = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Protocol);
158     while (e) {
159         string id = XMLHelper::getAttrString(e, nullptr, _id);
160         if (!id.empty()) {
161             const DOMElement* svc = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Service);
162             while (svc) {
163                 string svcid = XMLHelper::getAttrString(svc, nullptr, _id);
164                 if (!svcid.empty() && m_map.count(make_pair(id,svcid)) == 0) {
165                     pair< const PropertySet*,vector<const PropertySet*> >& entry = m_map[make_pair(id,svcid)];
166                     // Wrap the Initiator in a propset, if any.
167                     const DOMElement* child = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Initiator);
168                     if (child) {
169                         boost::shared_ptr<DOMPropertySet> initprop(new DOMPropertySet());
170                         initprop->load(child, nullptr, this);
171                         m_propsetJanitor.push_back(initprop);
172                         entry.first = initprop.get();
173                     }
174                     else {
175                         entry.first = nullptr;
176                     }
177
178                     // Walk the Bindings.
179                     child = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Binding);
180                     while (child) {
181                         boost::shared_ptr<DOMPropertySet> bindprop(new DOMPropertySet());
182                         bindprop->load(child, nullptr, this);
183                         m_propsetJanitor.push_back(bindprop);
184                         entry.second.push_back(bindprop.get());
185                         child = XMLHelper::getNextSiblingElement(child, SHIB2SPPROTOCOLS_NS, Binding);
186                     }
187                 }
188                 svc = XMLHelper::getNextSiblingElement(svc, SHIB2SPPROTOCOLS_NS, Service);
189             }
190         }
191         e = XMLHelper::getNextSiblingElement(e, SHIB2SPPROTOCOLS_NS, Protocol);
192     }
193 }
194
195
196 pair<bool,DOMElement*> XMLProtocolProvider::load(bool backup)
197 {
198     // Load from source using base class.
199     pair<bool,DOMElement*> raw = ReloadableXMLFile::load(backup);
200
201     // If we own it, wrap it.
202     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
203
204     scoped_ptr<XMLProtocolProviderImpl> impl(new XMLProtocolProviderImpl(raw.second, m_log));
205
206     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
207     impl->setDocument(docjanitor.release());
208
209     // Perform the swap inside a lock.
210     if (m_lock)
211         m_lock->wrlock();
212     SharedLock locker(m_lock, false);
213     m_impl.swap(impl);
214
215
216     return make_pair(false,(DOMElement*)nullptr);
217 }
218
219 pair<bool,DOMElement*> XMLProtocolProvider::background_load()
220 {
221     try {
222         return load(false);
223     }
224     catch (long& ex) {
225         if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
226             m_log.info("remote resource (%s) unchanged", m_source.c_str());
227         if (!m_loaded && !m_backing.empty())
228             return load(true);
229         throw;
230     }
231     catch (std::exception&) {
232         if (!m_loaded && !m_backing.empty())
233             return load(true);
234         throw;
235     }
236 }