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  * DOMPropertySet.cpp
23  *
24  * DOM-based property set implementation.
25  */
26 
27 #include "internal.h"
28 #include "util/DOMPropertySet.h"
29 
30 #include <algorithm>
31 #include <boost/lexical_cast.hpp>
32 #include <boost/algorithm/string.hpp>
33 #include <xmltooling/util/NDC.h>
34 #include <xmltooling/util/XMLConstants.h>
35 #include <xmltooling/util/XMLHelper.h>
36 
37 using namespace shibsp;
38 using namespace xmltooling;
39 using namespace xercesc;
40 using namespace boost;
41 using namespace std;
42 
PropertySet()43 PropertySet::PropertySet()
44 {
45 }
46 
~PropertySet()47 PropertySet::~PropertySet()
48 {
49 }
50 
Remapper()51 DOMPropertySet::Remapper::Remapper()
52 {
53 }
54 
~Remapper()55 DOMPropertySet::Remapper::~Remapper()
56 {
57 }
58 
STLRemapper(const std::map<std::string,std::string> & rules)59 DOMPropertySet::STLRemapper::STLRemapper(const std::map<std::string, std::string>& rules) : m_rules(rules)
60 {
61 }
62 
~STLRemapper()63 DOMPropertySet::STLRemapper::~STLRemapper()
64 {
65 }
66 
remap(const char * src,xmltooling::logging::Category & log) const67 const char* DOMPropertySet::STLRemapper::remap(const char* src, xmltooling::logging::Category& log) const
68 {
69     map<string,string>::const_iterator i = src ? m_rules.find(src) : m_rules.end();
70     if (i != m_rules.end()) {
71         SPConfig::getConfig().deprecation().warn("legacy configuration, remapping property/set (%s) to (%s)", src, i->second.c_str());
72         return i->second.c_str();
73     }
74     else {
75         return src;
76     }
77 }
78 
DOMPropertySet()79 DOMPropertySet::DOMPropertySet() : m_parent(nullptr), m_root(nullptr)
80 {
81 }
82 
~DOMPropertySet()83 DOMPropertySet::~DOMPropertySet()
84 {
85     for (map<string,pair<char*,const XMLCh*> >::iterator i = m_map.begin(); i != m_map.end(); ++i)
86         XMLString::release(&(i->second.first));
87 }
88 
getParent() const89 const PropertySet* DOMPropertySet::getParent() const
90 {
91     return m_parent;
92 }
93 
setParent(const PropertySet * parent)94 void DOMPropertySet::setParent(const PropertySet* parent)
95 {
96     m_parent = parent;
97 }
98 
getElement() const99 const DOMElement* DOMPropertySet::getElement() const
100 {
101     return m_root;
102 }
103 
load(const DOMElement * e,Category * log,DOMNodeFilter * filter,const Remapper * remapper,const xmltooling::QName * unsetter)104 void DOMPropertySet::load(
105     const DOMElement* e,
106     Category* log,
107     DOMNodeFilter* filter,
108     const Remapper* remapper,
109     const xmltooling::QName* unsetter
110     )
111 {
112 #ifdef _DEBUG
113     NDC ndc("load");
114 #endif
115     if (!e)
116         return;
117     m_root=e;
118     if (!log)
119         log = &Category::getInstance(SHIBSP_LOGCAT ".PropertySet");
120 
121     // Process each attribute as a property.
122     DOMNamedNodeMap* attrs=m_root->getAttributes();
123     for (XMLSize_t i=0; i<attrs->getLength(); i++) {
124         DOMNode* a=attrs->item(i);
125         if (!XMLString::compareString(a->getNamespaceURI(), xmlconstants::XMLNS_NS)) {
126             continue;
127         }
128         else if (unsetter && XMLHelper::isNodeNamed(a, unsetter->getNamespaceURI(), unsetter->getLocalPart())) {
129             auto_ptr_char val(a->getNodeValue());
130             string dup(val.get());
131             split(m_unset, dup, is_space(), algorithm::token_compress_on);
132             continue;
133         }
134 
135         char* val=XMLString::transcode(a->getNodeValue());
136         if (val && *val) {
137             auto_ptr_char ns(a->getNamespaceURI());
138             auto_ptr_char name(a->getLocalName());
139             const char* realname=name.get();
140             if (remapper) {
141                 realname = remapper->remap(realname, *log);
142             }
143             if (ns.get()) {
144                 const char* realns = ns.get();
145                 if (remapper) {
146                     realns = remapper->remap(realns, *log);
147                 }
148                 else if (XMLString::equals(realns, shibspconstants::ASCII_SHIB2SPCONFIG_NS)) {
149                     realns = shibspconstants::ASCII_SHIB3SPCONFIG_NS;
150                 }
151                 m_map[string("{") + realns + '}' + realname] = pair<char*, const XMLCh*>(val, a->getNodeValue());
152                 log->debug("added property {%s}%s (%s)", realns, realname, val);
153             }
154             else {
155                 m_map[realname]=pair<char*,const XMLCh*>(val,a->getNodeValue());
156                 log->debug("added property %s (%s)", realname, val);
157             }
158         }
159     }
160 
161     // Process non-excluded elements as nested sets.
162     DOMTreeWalker* walker =
163         static_cast<DOMDocumentTraversal*>(
164             m_root->getOwnerDocument())->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,filter,false
165             );
166     e = static_cast<DOMElement*>(walker->firstChild());
167     while (e) {
168         auto_ptr_char ns(e->getNamespaceURI());
169         auto_ptr_char name(e->getLocalName());
170         const char* realname=name.get();
171         if (remapper) {
172             realname = remapper->remap(realname, *log);
173         }
174         string key;
175         if (ns.get()) {
176             const char* realns = ns.get();
177             if (remapper) {
178                 realns = remapper->remap(realns, *log);
179             }
180             else if (XMLString::equals(realns, shibspconstants::ASCII_SHIB2SPCONFIG_NS)) {
181                 realns = shibspconstants::ASCII_SHIB3SPCONFIG_NS;
182             }
183             key = string("{") + realns + '}' + realname;
184         }
185         else {
186             key = realname;
187         }
188         if (m_nested.find(key) != m_nested.end())
189             log->warn("load() skipping duplicate property set: %s", key.c_str());
190         else {
191             boost::shared_ptr<DOMPropertySet> newset(new DOMPropertySet());
192             newset->load(e,log,filter,remapper);
193             m_nested[key] = newset;
194             log->debug("added nested property set: %s", key.c_str());
195         }
196         e = static_cast<DOMElement*>(walker->nextSibling());
197     }
198     walker->release();
199 }
200 
getBool(const char * name,const char * ns) const201 pair<bool,bool> DOMPropertySet::getBool(const char* name, const char* ns) const
202 {
203     map< string,pair<char*,const XMLCh*> >::const_iterator i;
204 
205     if (ns)
206         i=m_map.find(string("{") + ns + '}' + name);
207     else
208         i=m_map.find(name);
209 
210 
211     if (i!=m_map.end())
212         return make_pair(true,(!strcmp(i->second.first,"true") || !strcmp(i->second.first,"1")));
213     else if (m_parent && m_unset.find(ns ? (string("{") + ns + '}' + name) : name) == m_unset.end()) {
214         return m_parent->getBool(name, ns);
215     }
216     return make_pair(false,false);
217 }
218 
getString(const char * name,const char * ns) const219 pair<bool,const char*> DOMPropertySet::getString(const char* name, const char* ns) const
220 {
221     map< string,pair<char*,const XMLCh*> >::const_iterator i;
222 
223     if (ns)
224         i=m_map.find(string("{") + ns + '}' + name);
225     else
226         i=m_map.find(name);
227 
228     if (i!=m_map.end())
229         return pair<bool,const char*>(true,i->second.first);
230     else if (m_parent && m_unset.find(ns ? (string("{") + ns + '}' + name) : name) == m_unset.end())
231         return m_parent->getString(name,ns);
232     return pair<bool,const char*>(false,nullptr);
233 }
234 
getXMLString(const char * name,const char * ns) const235 pair<bool,const XMLCh*> DOMPropertySet::getXMLString(const char* name, const char* ns) const
236 {
237     map< string,pair<char*,const XMLCh*> >::const_iterator i;
238 
239     if (ns)
240         i=m_map.find(string("{") + ns + '}' + name);
241     else
242         i=m_map.find(name);
243 
244     if (i!=m_map.end())
245         return make_pair(true,i->second.second);
246     else if (m_parent && m_unset.find(ns ? (string("{") + ns + '}' + name) : name) == m_unset.end())
247         return m_parent->getXMLString(name,ns);
248     return pair<bool,const XMLCh*>(false,nullptr);
249 }
250 
getUnsignedInt(const char * name,const char * ns) const251 pair<bool,unsigned int> DOMPropertySet::getUnsignedInt(const char* name, const char* ns) const
252 {
253     map< string,pair<char*,const XMLCh*> >::const_iterator i;
254 
255     if (ns)
256         i=m_map.find(string("{") + ns + '}' + name);
257     else
258         i=m_map.find(name);
259 
260     if (i!=m_map.end()) {
261         try {
262             return pair<bool,unsigned int>(true,lexical_cast<unsigned int>(i->second.first));
263         }
264         catch (bad_lexical_cast&) {
265             return pair<bool,unsigned int>(false,0);
266         }
267     }
268     else if (m_parent && m_unset.find(ns ? (string("{") + ns + '}' + name) : name) == m_unset.end())
269         return m_parent->getUnsignedInt(name,ns);
270     return pair<bool,unsigned int>(false,0);
271 }
272 
getInt(const char * name,const char * ns) const273 pair<bool,int> DOMPropertySet::getInt(const char* name, const char* ns) const
274 {
275     map< string,pair<char*,const XMLCh*> >::const_iterator i;
276 
277     if (ns)
278         i=m_map.find(string("{") + ns + '}' + name);
279     else
280         i=m_map.find(name);
281 
282     if (i!=m_map.end())
283         return pair<bool,int>(true,atoi(i->second.first));
284     else if (m_parent && m_unset.find(ns ? (string("{") + ns + '}' + name) : name) == m_unset.end())
285         return m_parent->getInt(name,ns);
286     return pair<bool,int>(false,0);
287 }
288 
getPropertySet(const char * name,const char * ns) const289 const PropertySet* DOMPropertySet::getPropertySet(const char* name, const char* ns) const
290 {
291     map< string,boost::shared_ptr<DOMPropertySet> >::const_iterator i;
292 
293     if (ns)
294         i = m_nested.find(string("{") + ns + '}' + name);
295     else
296         i = m_nested.find(name);
297 
298     return (i != m_nested.end()) ? i->second.get() : (m_parent ? m_parent->getPropertySet(name,ns) : nullptr);
299 }
300 
setProperty(const char * name,const char * val,const char * ns)301 bool DOMPropertySet::setProperty(const char* name, const char* val, const char* ns)
302 {
303     string propname = ns ? (string("{") + ns + "}" + name) : name;
304 
305     // Erase existing property.
306     if (m_map.count(propname) > 0) {
307         XMLString::release(&m_map[propname].first);
308         m_map.erase(propname);
309     }
310 
311     char* dup = XMLString::replicate(val);
312     auto_ptr_XMLCh widedup(val);
313     m_injected.push_back(widedup.get());
314     m_map[propname] = make_pair(dup, m_injected.back().c_str());
315 
316     return true;
317 }
318