Added application "hash".
[shibboleth/cpp-sp.git] / shib-target / shib-ini.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /*
51  * shib-ini.h -- config file handling, now XML-based
52  *
53  * $Id$
54  */
55
56 #include "internal.h"
57
58 #include <shib/shib-threads.h>
59 #include <log4cpp/Category.hh>
60 #include <log4cpp/PropertyConfigurator.hh>
61
62 #include <sys/types.h>
63 #include <sys/stat.h>
64
65 using namespace std;
66 using namespace saml;
67 using namespace shibboleth;
68 using namespace shibtarget;
69 using namespace log4cpp;
70
71 namespace shibtarget {
72
73     // Application configuration wrapper
74     class XMLApplication : public virtual IApplication, public XMLPropertySet, public DOMNodeFilter
75     {
76     public:
77         XMLApplication(const IConfig*, const Iterator<ICredentials*>& creds, const DOMElement* e, const XMLApplication* base=NULL);
78         ~XMLApplication() { cleanup(); }
79     
80         // IPropertySet
81         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
82         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
83         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
84         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;
85         pair<bool,int> getInt(const char* name, const char* ns=NULL) const;
86         const IPropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;
87
88         // IApplication
89         const char* getId() const {return getString("id").second;}
90         const char* getHash() const {return m_hash.c_str();}
91         Iterator<SAMLAttributeDesignator*> getAttributeDesignators() const;
92         Iterator<IAAP*> getAAPProviders() const;
93         Iterator<IMetadata*> getMetadataProviders() const;
94         Iterator<ITrust*> getTrustProviders() const;
95         Iterator<IRevocation*> getRevocationProviders() const;
96         Iterator<const XMLCh*> getAudiences() const;
97         const IPropertySet* getCredentialUse(const IEntityDescriptor* provider) const;
98         const SAMLBrowserProfile* getBrowserProfile() const {return m_profile;}
99         const SAMLBinding* getBinding(const XMLCh* binding) const
100             {return XMLString::compareString(SAMLBinding::SOAP,binding) ? NULL : m_binding;}
101         SAMLBrowserProfile::ArtifactMapper* getArtifactMapper() const {return new STArtifactMapper(this);}
102         
103         // Provides filter to exclude special config elements.
104         short acceptNode(const DOMNode* node) const;
105     
106     private:
107         void cleanup();
108         const IConfig* m_ini;   // this is ok because its locking scope includes us
109         const XMLApplication* m_base;
110         string m_hash;
111         vector<SAMLAttributeDesignator*> m_designators;
112         vector<IAAP*> m_aaps;
113         vector<IMetadata*> m_metadatas;
114         vector<ITrust*> m_trusts;
115         vector<IRevocation*> m_revocations;
116         vector<const XMLCh*> m_audiences;
117         ShibBrowserProfile* m_profile;
118         SAMLBinding* m_binding;
119         ShibHTTPHook* m_bindingHook;
120         XMLPropertySet* m_credDefault;
121 #ifdef HAVE_GOOD_STL
122         map<xstring,XMLPropertySet*> m_credMap;
123 #else
124         map<const XMLCh*,XMLPropertySet*> m_credMap;
125 #endif
126     };
127
128     // Top-level configuration implementation
129     class XMLConfig;
130     class XMLConfigImpl : public ReloadableXMLFileImpl, public XMLPropertySet, public DOMNodeFilter
131     {
132     public:
133         XMLConfigImpl(const char* pathname, bool first, const XMLConfig* outer)
134             : ReloadableXMLFileImpl(pathname), m_outer(outer), m_requestMapper(NULL) { init(first); }
135         XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer)
136             : ReloadableXMLFileImpl(e), m_outer(outer), m_requestMapper(NULL) { init(first); }
137         ~XMLConfigImpl();
138         
139         IRequestMapper* m_requestMapper;
140         map<string,IApplication*> m_appmap;
141         vector<ICredentials*> m_creds;
142         
143         // Provides filter to exclude special config elements.
144         short acceptNode(const DOMNode* node) const;
145
146     private:
147         void init(bool first);
148         const XMLConfig* m_outer;
149     };
150     
151     class XMLConfig : public IConfig, public ReloadableXMLFile
152     {
153     public:
154         XMLConfig(const DOMElement* e) : ReloadableXMLFile(e), m_listener(NULL), m_sessionCache(NULL), m_replayCache(NULL) {}
155         ~XMLConfig() {delete m_listener; delete m_sessionCache; delete m_replayCache;}
156
157         // IPropertySet
158         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getBool(name,ns);}
159         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getString(name,ns);}
160         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getXMLString(name,ns);}
161         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getUnsignedInt(name,ns);}
162         pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return static_cast<XMLConfigImpl*>(m_impl)->getInt(name,ns);}
163         const IPropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const {return static_cast<XMLConfigImpl*>(m_impl)->getPropertySet(name,ns);}
164         const DOMElement* getElement() const {return static_cast<XMLConfigImpl*>(m_impl)->getElement();}
165
166         // IConfig
167         const IListener* getListener() const {return m_listener;}
168         ISessionCache* getSessionCache() const {return m_sessionCache;}
169         IReplayCache* getReplayCache() const {return m_replayCache;}
170         IRequestMapper* getRequestMapper() const {return static_cast<XMLConfigImpl*>(m_impl)->m_requestMapper;}
171         const IApplication* getApplication(const char* applicationId) const
172         {
173             map<string,IApplication*>::const_iterator i=static_cast<XMLConfigImpl*>(m_impl)->m_appmap.find(applicationId);
174             return (i!=static_cast<XMLConfigImpl*>(m_impl)->m_appmap.end()) ? i->second : NULL;
175         }
176         Iterator<ICredentials*> getCredentialsProviders() const {return static_cast<XMLConfigImpl*>(m_impl)->m_creds;}
177
178     protected:
179         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
180         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
181
182     private:
183         friend class XMLConfigImpl;
184         mutable IListener* m_listener;
185         mutable ISessionCache* m_sessionCache;
186         mutable IReplayCache* m_replayCache;
187     };
188 }
189
190 IConfig* STConfig::ShibTargetConfigFactory(const DOMElement* e)
191 {
192     auto_ptr<XMLConfig> ret(new XMLConfig(e));
193     ret->getImplementation();
194     return ret.release();
195 }
196
197 XMLPropertySet::~XMLPropertySet()
198 {
199     for (map<string,pair<char*,const XMLCh*> >::iterator i=m_map.begin(); i!=m_map.end(); i++)
200         XMLString::release(&(i->second.first));
201     for (map<string,IPropertySet*>::iterator j=m_nested.begin(); j!=m_nested.end(); j++)
202         delete j->second;
203 }
204
205 void XMLPropertySet::load(
206     const DOMElement* e,
207     Category& log,
208     DOMNodeFilter* filter,
209     const std::map<std::string,std::string>* remapper
210     )
211 {
212 #ifdef _DEBUG
213     saml::NDC ndc("load");
214 #endif
215     m_root=e;
216
217     // Process each attribute as a property.
218     DOMNamedNodeMap* attrs=m_root->getAttributes();
219     for (XMLSize_t i=0; i<attrs->getLength(); i++) {
220         DOMNode* a=attrs->item(i);
221         if (!XMLString::compareString(a->getNamespaceURI(),saml::XML::XMLNS_NS))
222             continue;
223         char* val=XMLString::transcode(a->getNodeValue());
224         if (val && *val) {
225             auto_ptr_char ns(a->getNamespaceURI());
226             auto_ptr_char name(a->getLocalName());
227             const char* realname=name.get();
228             if (remapper) {
229                 map<string,string>::const_iterator remap=remapper->find(realname);
230                 if (remap!=remapper->end()) {
231                     log.debug("remapping property (%s) to (%s)",realname,remap->second.c_str());
232                     realname=remap->second.c_str();
233                 }
234             }
235             if (ns.get()) {
236                 m_map[string("{") + ns.get() + '}' + realname]=pair<char*,const XMLCh*>(val,a->getNodeValue());
237                 log.debug("added property {%s}%s (%s)",ns.get(),realname,val);
238             }
239             else {
240                 m_map[realname]=pair<char*,const XMLCh*>(val,a->getNodeValue());
241                 log.debug("added property %s (%s)",realname,val);
242             }
243         }
244     }
245     
246     // Process non-excluded elements as nested sets.
247     DOMTreeWalker* walker=
248         static_cast<DOMDocumentTraversal*>(
249             m_root->getOwnerDocument())->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,filter,false
250             );
251     e=static_cast<DOMElement*>(walker->firstChild());
252     while (e) {
253         auto_ptr_char ns(e->getNamespaceURI());
254         auto_ptr_char name(e->getLocalName());
255         const char* realname=name.get();
256         if (remapper) {
257             map<string,string>::const_iterator remap=remapper->find(realname);
258             if (remap!=remapper->end()) {
259                 log.debug("remapping property set (%s) to (%s)",realname,remap->second.c_str());
260                 realname=remap->second.c_str();
261             }
262         }
263         string key;
264         if (ns.get())
265             key=string("{") + ns.get() + '}' + realname;
266         else
267             key=realname;
268         if (m_nested.find(key)!=m_nested.end())
269             log.warn("load() skipping duplicate property set: %s",key.c_str());
270         else {
271             XMLPropertySet* set=new XMLPropertySet();
272             set->load(e,log,filter,remapper);
273             m_nested[key]=set;
274             log.debug("added nested property set: %s",key.c_str());
275         }
276         e=static_cast<DOMElement*>(walker->nextSibling());
277     }
278     walker->release();
279 }
280
281 pair<bool,bool> XMLPropertySet::getBool(const char* name, const char* ns) const
282 {
283     pair<bool,bool> ret=pair<bool,bool>(false,false);
284     map<string,pair<char*,const XMLCh*> >::const_iterator i;
285
286     if (ns)
287         i=m_map.find(string("{") + ns + '}' + name);
288     else
289         i=m_map.find(name);
290
291     if (i!=m_map.end()) {
292         ret.first=true;
293         ret.second=(!strcmp(i->second.first,"true") || !strcmp(i->second.first,"1"));
294     }
295     return ret;
296 }
297
298 pair<bool,const char*> XMLPropertySet::getString(const char* name, const char* ns) const
299 {
300     pair<bool,const char*> ret=pair<bool,const char*>(false,NULL);
301     map<string,pair<char*,const XMLCh*> >::const_iterator i;
302
303     if (ns)
304         i=m_map.find(string("{") + ns + '}' + name);
305     else
306         i=m_map.find(name);
307
308     if (i!=m_map.end()) {
309         ret.first=true;
310         ret.second=i->second.first;
311     }
312     return ret;
313 }
314
315 pair<bool,const XMLCh*> XMLPropertySet::getXMLString(const char* name, const char* ns) const
316 {
317     pair<bool,const XMLCh*> ret=pair<bool,const XMLCh*>(false,NULL);
318     map<string,pair<char*,const XMLCh*> >::const_iterator i;
319
320     if (ns)
321         i=m_map.find(string("{") + ns + '}' + name);
322     else
323         i=m_map.find(name);
324
325     if (i!=m_map.end()) {
326         ret.first=true;
327         ret.second=i->second.second;
328     }
329     return ret;
330 }
331
332 pair<bool,unsigned int> XMLPropertySet::getUnsignedInt(const char* name, const char* ns) const
333 {
334     pair<bool,unsigned int> ret=pair<bool,unsigned int>(false,0);
335     map<string,pair<char*,const XMLCh*> >::const_iterator i;
336
337     if (ns)
338         i=m_map.find(string("{") + ns + '}' + name);
339     else
340         i=m_map.find(name);
341
342     if (i!=m_map.end()) {
343         ret.first=true;
344         ret.second=strtol(i->second.first,NULL,10);
345     }
346     return ret;
347 }
348
349 pair<bool,int> XMLPropertySet::getInt(const char* name, const char* ns) const
350 {
351     pair<bool,int> ret=pair<bool,int>(false,0);
352     map<string,pair<char*,const XMLCh*> >::const_iterator i;
353
354     if (ns)
355         i=m_map.find(string("{") + ns + '}' + name);
356     else
357         i=m_map.find(name);
358
359     if (i!=m_map.end()) {
360         ret.first=true;
361         ret.second=atoi(i->second.first);
362     }
363     return ret;
364 }
365
366 const IPropertySet* XMLPropertySet::getPropertySet(const char* name, const char* ns) const
367 {
368     map<string,IPropertySet*>::const_iterator i;
369
370     if (ns)
371         i=m_nested.find(string("{") + ns + '}' + name);
372     else
373         i=m_nested.find(name);
374
375     return (i!=m_nested.end()) ? i->second : NULL;
376 }
377
378 XMLApplication::XMLApplication(const IConfig* ini, const Iterator<ICredentials*>& creds, const DOMElement* e, const XMLApplication* base)
379     : m_ini(ini), m_base(base), m_profile(NULL), m_binding(NULL), m_bindingHook(NULL), m_credDefault(NULL)
380 {
381 #ifdef _DEBUG
382     NDC ndc("XMLApplication");
383 #endif
384     Category& log=Category::getInstance("shibtarget.XMLApplication");
385
386     try {
387         // First load any property sets.
388         map<string,string> root_remap;
389         root_remap["shire"]="session";
390         load(e,log,this,&root_remap);
391         const IPropertySet* propcheck=getPropertySet("Errors");
392         if (propcheck && !propcheck->getString("session").first)
393             throw MalformedException("<Errors> element requires 'session' (or deprecated 'shire') attribute");
394
395         m_hash=getId();
396         m_hash+=getString("providerId").second;
397         m_hash=SAMLArtifact::toHex(SAMLArtifactType0001::generateSourceId(m_hash.c_str()));
398
399         ShibTargetConfig& conf=ShibTargetConfig::getConfig();
400         SAMLConfig& shibConf=SAMLConfig::getConfig();
401         int i;
402         DOMNodeList* nlist=e->getElementsByTagNameNS(saml::XML::SAML_NS,L(AttributeDesignator));
403         for (i=0; nlist && i<nlist->getLength(); i++) {
404             m_designators.push_back(new SAMLAttributeDesignator(static_cast<DOMElement*>(nlist->item(i))));
405         }
406
407         nlist=e->getElementsByTagNameNS(saml::XML::SAML_NS,L(Audience));
408         for (i=0; nlist && i<nlist->getLength(); i++) {
409             m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
410         }
411         // Always include our own providerId as an audience.
412         m_audiences.push_back(getXMLString("providerId").second);
413
414         if (conf.isEnabled(ShibTargetConfig::AAP)) {
415             nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(AAPProvider));
416             for (i=0; nlist && i<nlist->getLength(); i++) {
417                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
418                 log.info("building AAP provider of type %s...",type.get());
419                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
420                 IAAP* aap=dynamic_cast<IAAP*>(plugin);
421                 if (aap)
422                     m_aaps.push_back(aap);
423                 else {
424                     delete plugin;
425                     log.fatal("plugin was not an AAP provider");
426                     throw UnsupportedExtensionException("plugin was not an AAP provider");
427                 }
428             }
429         }
430
431         if (conf.isEnabled(ShibTargetConfig::Metadata)) {
432             nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(FederationProvider));
433             for (i=0; nlist && i<nlist->getLength(); i++) {
434                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
435                 log.info("building federation/metadata provider of type %s...",type.get());
436                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
437                 IMetadata* md=dynamic_cast<IMetadata*>(plugin);
438                 if (md)
439                     m_metadatas.push_back(md);
440                 else {
441                     delete plugin;
442                     log.fatal("plugin was not a federation/metadata provider");
443                     throw UnsupportedExtensionException("plugin was not a federation/metadata provider");
444                 }
445             }
446         }
447
448         if (conf.isEnabled(ShibTargetConfig::Trust)) {
449             nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(TrustProvider));
450             for (i=0; nlist && i<nlist->getLength(); i++) {
451                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
452                 log.info("building trust provider of type %s...",type.get());
453                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
454                 ITrust* trust=dynamic_cast<ITrust*>(plugin);
455                 if (trust)
456                     m_trusts.push_back(trust);
457                 else {
458                     delete plugin;
459                     log.fatal("plugin was not a trust provider");
460                     throw UnsupportedExtensionException("plugin was not a trust provider");
461                 }
462             }
463             nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RevocationProvider));
464             for (i=0; nlist && i<nlist->getLength(); i++) {
465                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
466                 log.info("building revocation provider of type %s...",type.get());
467                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
468                 IRevocation* rev=dynamic_cast<IRevocation*>(plugin);
469                 if (rev)
470                     m_revocations.push_back(rev);
471                 else {
472                     delete plugin;
473                     log.fatal("plugin was not a revocation provider");
474                     throw UnsupportedExtensionException("plugin was not a revocation provider");
475                 }
476             }
477         }
478         
479         // Finally, load credential mappings.
480         const DOMElement* cu=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(CredentialUse));
481         if (cu) {
482             m_credDefault=new XMLPropertySet();
483             m_credDefault->load(cu,log,this);
484             cu=saml::XML::getFirstChildElement(cu,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RelyingParty));
485             while (cu) {
486                 XMLPropertySet* rp=new XMLPropertySet();
487                 rp->load(cu,log,this);
488                 m_credMap[cu->getAttributeNS(NULL,SHIBT_L(Name))]=rp;
489                 cu=saml::XML::getNextSiblingElement(cu,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RelyingParty));
490             }
491         }
492         
493         if (conf.isEnabled(ShibTargetConfig::Caching)) {
494             // Really finally, build local browser profile and binding objects.
495             m_profile=new ShibBrowserProfile(
496                 getMetadataProviders(),
497                 getRevocationProviders(),
498                 getTrustProviders()
499                 );
500             m_bindingHook=new ShibHTTPHook(
501                 getRevocationProviders(),
502                 getTrustProviders(),
503                 creds
504                 );
505             m_binding=SAMLBinding::getInstance(SAMLBinding::SOAP);
506             SAMLSOAPHTTPBinding* bptr=dynamic_cast<SAMLSOAPHTTPBinding*>(m_binding);
507             if (!bptr) {
508                 log.fatal("binding implementation was not SOAP over HTTP");
509                 throw UnsupportedExtensionException("binding implementation was not SOAP over HTTP");
510             }
511             bptr->addHook(m_bindingHook,m_bindingHook); // the hook is its own global context
512         }
513     }
514     catch (SAMLException& e) {
515         log.errorStream() << "Error while processing applicaton element: " << e.what() << CategoryStream::ENDLINE;
516         cleanup();
517         throw;
518     }
519 #ifndef _DEBUG
520     catch (...) {
521         log.error("Unexpected error while processing application element");
522         cleanup();
523         throw;
524     }
525 #endif
526 }
527
528 void XMLApplication::cleanup()
529 {
530     delete m_bindingHook;
531     delete m_binding;
532     delete m_profile;
533     delete m_credDefault;
534 #ifdef HAVE_GOOD_STL
535     map<xstring,XMLPropertySet*>::iterator c=m_credMap.begin();
536 #else
537     map<const XMLCh*,XMLPropertySet*>::iterator c=m_credMap.begin();
538 #endif
539     while (c!=m_credMap.end()) {
540         delete c->second;
541         c++;
542     }
543     Iterator<SAMLAttributeDesignator*> i(m_designators);
544     while (i.hasNext())
545         delete i.next();
546     Iterator<IAAP*> j(m_aaps);
547     while (j.hasNext())
548         delete j.next();
549     Iterator<IMetadata*> k(m_metadatas);
550     while (k.hasNext())
551         delete k.next();
552     Iterator<ITrust*> l(m_trusts);
553     while (l.hasNext())
554         delete l.next();
555     Iterator<IRevocation*> m(m_revocations);
556     while (m.hasNext())
557         delete m.next();
558 }
559
560 short XMLApplication::acceptNode(const DOMNode* node) const
561 {
562     if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::SAML_NS,L(AttributeDesignator)))
563         return FILTER_REJECT;
564     else if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::SAML_NS,L(Audience)))
565         return FILTER_REJECT;
566     if (XMLString::compareString(node->getNamespaceURI(),ShibTargetConfig::SHIBTARGET_NS))
567         return FILTER_ACCEPT;
568     const XMLCh* name=node->getLocalName();
569     if (!XMLString::compareString(name,SHIBT_L(Application)) ||
570         !XMLString::compareString(name,SHIBT_L(AAPProvider)) ||
571         !XMLString::compareString(name,SHIBT_L(CredentialUse)) ||
572         !XMLString::compareString(name,SHIBT_L(RelyingParty)) ||
573         !XMLString::compareString(name,SHIBT_L(FederationProvider)) ||
574         !XMLString::compareString(name,SHIBT_L(RevocationProvider)) ||
575         !XMLString::compareString(name,SHIBT_L(TrustProvider)))
576         return FILTER_REJECT;
577
578     return FILTER_ACCEPT;
579 }
580
581 pair<bool,bool> XMLApplication::getBool(const char* name, const char* ns) const
582 {
583     pair<bool,bool> ret=XMLPropertySet::getBool(name,ns);
584     if (ret.first)
585         return ret;
586     return m_base ? m_base->getBool(name,ns) : ret;
587 }
588
589 pair<bool,const char*> XMLApplication::getString(const char* name, const char* ns) const
590 {
591     pair<bool,const char*> ret=XMLPropertySet::getString(name,ns);
592     if (ret.first)
593         return ret;
594     return m_base ? m_base->getString(name,ns) : ret;
595 }
596
597 pair<bool,const XMLCh*> XMLApplication::getXMLString(const char* name, const char* ns) const
598 {
599     pair<bool,const XMLCh*> ret=XMLPropertySet::getXMLString(name,ns);
600     if (ret.first)
601         return ret;
602     return m_base ? m_base->getXMLString(name,ns) : ret;
603 }
604
605 pair<bool,unsigned int> XMLApplication::getUnsignedInt(const char* name, const char* ns) const
606 {
607     pair<bool,unsigned int> ret=XMLPropertySet::getUnsignedInt(name,ns);
608     if (ret.first)
609         return ret;
610     return m_base ? m_base->getUnsignedInt(name,ns) : ret;
611 }
612
613 pair<bool,int> XMLApplication::getInt(const char* name, const char* ns) const
614 {
615     pair<bool,int> ret=XMLPropertySet::getInt(name,ns);
616     if (ret.first)
617         return ret;
618     return m_base ? m_base->getInt(name,ns) : ret;
619 }
620
621 const IPropertySet* XMLApplication::getPropertySet(const char* name, const char* ns) const
622 {
623     const IPropertySet* ret=XMLPropertySet::getPropertySet(name,ns);
624     if (ret || !m_base)
625         return ret;
626     return m_base->getPropertySet(name,ns);
627 }
628
629 Iterator<SAMLAttributeDesignator*> XMLApplication::getAttributeDesignators() const
630 {
631     if (!m_designators.empty() || !m_base)
632         return m_designators;
633     return m_base->getAttributeDesignators();
634 }
635
636 Iterator<IAAP*> XMLApplication::getAAPProviders() const
637 {
638     return (m_aaps.empty() && m_base) ? m_base->getAAPProviders() : m_aaps;
639 }
640
641 Iterator<IMetadata*> XMLApplication::getMetadataProviders() const
642 {
643     return (m_metadatas.empty() && m_base) ? m_base->getMetadataProviders() : m_metadatas;
644 }
645
646 Iterator<ITrust*> XMLApplication::getTrustProviders() const
647 {
648     return (m_trusts.empty() && m_base) ? m_base->getTrustProviders() : m_trusts;
649 }
650
651 Iterator<IRevocation*> XMLApplication::getRevocationProviders() const
652 {
653     return (m_revocations.empty() && m_base) ? m_base->getRevocationProviders() : m_revocations;
654 }
655
656 Iterator<const XMLCh*> XMLApplication::getAudiences() const
657 {
658     return (m_audiences.empty() && m_base) ? m_base->getAudiences() : m_audiences;
659 }
660
661 const IPropertySet* XMLApplication::getCredentialUse(const IEntityDescriptor* provider) const
662 {
663     if (!m_credDefault && m_base)
664         return m_base->getCredentialUse(provider);
665         
666 #ifdef HAVE_GOOD_STL
667     map<xstring,XMLPropertySet*>::const_iterator i=m_credMap.find(provider->getId());
668     if (i!=m_credMap.end())
669         return i->second;
670     const IEntitiesDescriptor* group=provider->getEntitiesDescriptor();
671     while (group) {
672         i=m_credMap.find(group->getName());
673         if (i!=m_credMap.end())
674             return i->second;
675         group=group->getEntitiesDescriptor();
676     }
677 #else
678     map<const XMLCh*,XMLPropertySet*>::const_iterator i=m_credMap.begin();
679     for (; i!=m_credMap.end(); i++) {
680         if (!XMLString::compareString(i->first,provider->getId()))
681             return i->second;
682         const IEntitiesDescriptor* group=provider->getEntitiesDescriptor();
683         while (group) {
684             if (!XMLString::compareString(i->first,group->getName()))
685                 return i->second;
686             group=group->getEntitiesDescriptor();
687         }
688     }
689 #endif
690     return m_credDefault;
691 }
692
693 ReloadableXMLFileImpl* XMLConfig::newImplementation(const char* pathname, bool first) const
694 {
695     return new XMLConfigImpl(pathname,first,this);
696 }
697
698 ReloadableXMLFileImpl* XMLConfig::newImplementation(const DOMElement* e, bool first) const
699 {
700     return new XMLConfigImpl(e,first,this);
701 }
702
703 short XMLConfigImpl::acceptNode(const DOMNode* node) const
704 {
705     if (XMLString::compareString(node->getNamespaceURI(),ShibTargetConfig::SHIBTARGET_NS))
706         return FILTER_ACCEPT;
707     const XMLCh* name=node->getLocalName();
708     if (!XMLString::compareString(name,SHIBT_L(Applications)) ||
709         !XMLString::compareString(name,SHIBT_L(CredentialsProvider)) ||
710         !XMLString::compareString(name,SHIBT_L(Extensions)) ||
711         !XMLString::compareString(name,SHIBT_L(Implementation)) ||
712         !XMLString::compareString(name,SHIBT_L(Listener)) ||
713         !XMLString::compareString(name,SHIBT_L(MemorySessionCache)) ||
714         !XMLString::compareString(name,SHIBT_L(MySQLReplayCache)) ||
715         !XMLString::compareString(name,SHIBT_L(MySQLSessionCache)) ||
716         !XMLString::compareString(name,SHIBT_L(RequestMap)) ||
717         !XMLString::compareString(name,SHIBT_L(RequestMapProvider)) ||
718         !XMLString::compareString(name,SHIBT_L(ReplayCache)) ||
719         !XMLString::compareString(name,SHIBT_L(SessionCache)) ||
720         !XMLString::compareString(name,SHIBT_L(TCPListener)) ||
721         !XMLString::compareString(name,SHIBT_L(UnixListener)))
722         return FILTER_REJECT;
723
724     return FILTER_ACCEPT;
725 }
726
727 void XMLConfigImpl::init(bool first)
728 {
729 #ifdef _DEBUG
730     saml::NDC ndc("XMLConfigImpl");
731 #endif
732     Category& log=Category::getInstance("shibtarget.XMLConfig");
733
734     try {
735         if (!saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(ShibbolethTargetConfig)) &&
736             !saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(SPConfig))) {
737             log.error("Construction requires a valid configuration file: (conf:SPConfig as root element)");
738             throw MalformedException("Construction requires a valid configuration file: (conf:SPConfig as root element)");
739         }
740
741         SAMLConfig& shibConf=SAMLConfig::getConfig();
742         ShibTargetConfig& conf=ShibTargetConfig::getConfig();
743         const DOMElement* SHAR=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(SHAR));
744         if (!SHAR)
745             SHAR=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Global));
746         const DOMElement* SHIRE=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(SHIRE));
747         if (!SHIRE)
748             SHIRE=saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Local));
749
750         // Initialize log4cpp manually in order to redirect log messages as soon as possible.
751         if (conf.isEnabled(ShibTargetConfig::Logging)) {
752             const XMLCh* logger=NULL;
753             if (conf.isEnabled(ShibTargetConfig::GlobalExtensions))
754                 logger=SHAR->getAttributeNS(NULL,SHIBT_L(logger));
755             else if (conf.isEnabled(ShibTargetConfig::LocalExtensions))
756                 logger=SHIRE->getAttributeNS(NULL,SHIBT_L(logger));
757             if (!logger || !*logger)
758                 logger=ReloadableXMLFileImpl::m_root->getAttributeNS(NULL,SHIBT_L(logger));
759             if (logger && *logger) {
760                 auto_ptr_char logpath(logger);
761                 cerr << "loading new logging configuration from " << logpath.get() << "\n";
762                 try {
763                     PropertyConfigurator::configure(logpath.get());
764                     cerr << "New logging configuration loaded, check log destination for process status..." << "\n";
765                 }
766                 catch (ConfigureFailure& e) {
767                     cerr << "Error reading logging configuration: " << e.what() << "\n";
768                 }
769             }
770         }
771         
772         // First load any property sets.
773         map<string,string> root_remap;
774         root_remap["SHAR"]="Global";
775         root_remap["SHIRE"]="Local";
776         load(ReloadableXMLFileImpl::m_root,log,this,&root_remap);
777
778         // Much of the processing can only occur on the first instantiation.
779         if (first) {
780             // Now load any extensions to insure any needed plugins are registered.
781             DOMElement* exts=
782                 saml::XML::getFirstChildElement(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Extensions));
783             if (exts) {
784                 exts=saml::XML::getFirstChildElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
785                 while (exts) {
786                     auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
787                     try {
788                         SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
789                         log.debug("loaded global extension library %s",path.get());
790                     }
791                     catch (SAMLException& e) {
792                         const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
793                         if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
794                             log.fatal("unable to load mandatory global extension library %s: %s", path.get(), e.what());
795                             throw;
796                         }
797                         else
798                             log.crit("unable to load optional global extension library %s: %s", path.get(), e.what());
799                     }
800                     exts=saml::XML::getNextSiblingElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
801                 }
802             }
803             
804             if (conf.isEnabled(ShibTargetConfig::GlobalExtensions)) {
805                 exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Extensions));
806                 if (exts) {
807                     exts=saml::XML::getFirstChildElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
808                     while (exts) {
809                         auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
810                         try {
811                             SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
812                             log.debug("loaded Global extension library %s",path.get());
813                         }
814                         catch (SAMLException& e) {
815                             const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
816                             if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
817                                 log.fatal("unable to load mandatory Global extension library %s: %s", path.get(), e.what());
818                                 throw;
819                             }
820                             else
821                                 log.crit("unable to load optional Global extension library %s: %s", path.get(), e.what());
822                         }
823                         exts=saml::XML::getNextSiblingElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
824                     }
825                 }
826             }
827
828             if (conf.isEnabled(ShibTargetConfig::LocalExtensions)) {
829                 exts=saml::XML::getFirstChildElement(SHIRE,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Extensions));
830                 if (exts) {
831                     exts=saml::XML::getFirstChildElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
832                     while (exts) {
833                         auto_ptr_char path(exts->getAttributeNS(NULL,SHIBT_L(path)));
834                         try {
835                             SAMLConfig::getConfig().saml_register_extension(path.get(),exts);
836                             log.debug("loaded Local extension library %s",path.get());
837                         }
838                         catch (SAMLException& e) {
839                             const XMLCh* fatal=exts->getAttributeNS(NULL,SHIBT_L(fatal));
840                             if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
841                                 log.fatal("unable to load mandatory Local extension library %s: %s", path.get(), e.what());
842                                 throw;
843                             }
844                             else
845                                 log.crit("unable to load optional Local extension library %s: %s", path.get(), e.what());
846                         }
847                         exts=saml::XML::getNextSiblingElement(exts,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Library));
848                     }
849                 }
850             }
851             
852             // Instantiate the Listener and SessionCache objects.
853             if (conf.isEnabled(ShibTargetConfig::Listener)) {
854                 IPlugIn* plugin=NULL;
855                 exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(UnixListener));
856                 if (exts) {
857                     log.info("building Listener of type %s...",shibtarget::XML::UnixListenerType);
858                     plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::UnixListenerType,exts);
859                 }
860                 else {
861                     exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(TCPListener));
862                     if (exts) {
863                         log.info("building Listener of type %s...",shibtarget::XML::TCPListenerType);
864                         plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::TCPListenerType,exts);
865                     }
866                     else {
867                         exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Listener));
868                         if (exts) {
869                             auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
870                             log.info("building Listener of type %s...",type.get());
871                             plugin=shibConf.getPlugMgr().newPlugin(type.get(),exts);
872                         }
873                         else {
874                             log.fatal("can't build Listener object, missing conf:Listener element?");
875                             throw MalformedException("can't build Listener object, missing conf:Listener element?");
876                         }
877                     }
878                 }
879                 if (plugin) {
880                     IListener* listen=dynamic_cast<IListener*>(plugin);
881                     if (listen)
882                         m_outer->m_listener=listen;
883                     else {
884                         delete plugin;
885                         log.fatal("plugin was not a Listener object");
886                         throw UnsupportedExtensionException("plugin was not a Listener object");
887                     }
888                 }
889             }
890
891             if (conf.isEnabled(ShibTargetConfig::Caching)) {
892                 IPlugIn* plugin=NULL;
893                 exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(MemorySessionCache));
894                 if (exts) {
895                     log.info("building Session Cache of type %s...",shibtarget::XML::MemorySessionCacheType);
896                     plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::MemorySessionCacheType,exts);
897                 }
898                 else {
899                     exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(MySQLSessionCache));
900                     if (exts) {
901                         log.info("building Session Cache of type %s...",shibtarget::XML::MySQLSessionCacheType);
902                         plugin=shibConf.getPlugMgr().newPlugin(shibtarget::XML::MySQLSessionCacheType,exts);
903                     }
904                     else {
905                         exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(SessionCache));
906                         if (exts) {
907                             auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
908                             log.info("building Session Cache of type %s...",type.get());
909                             plugin=shibConf.getPlugMgr().newPlugin(type.get(),exts);
910                         }
911                         else {
912                             log.fatal("can't build Session Cache object, missing conf:SessionCache element?");
913                             throw MalformedException("can't build Session Cache object, missing conf:SessionCache element?");
914                         }
915                     }
916                 }
917                 if (plugin) {
918                     ISessionCache* cache=dynamic_cast<ISessionCache*>(plugin);
919                     if (cache)
920                         m_outer->m_sessionCache=cache;
921                     else {
922                         delete plugin;
923                         log.fatal("plugin was not a Session Cache object");
924                         throw UnsupportedExtensionException("plugin was not a Session Cache object");
925                     }
926                 }
927                 
928                 // Replay cache.
929                 exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(MySQLReplayCache));
930                 if (exts) {
931                     log.info("building Replay Cache of type %s...",shibtarget::XML::MySQLReplayCacheType);
932                     m_outer->m_replayCache=IReplayCache::getInstance(shibtarget::XML::MySQLSessionCacheType,exts);
933                 }
934                 else {
935                     exts=saml::XML::getFirstChildElement(SHAR,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(ReplayCache));
936                     if (exts) {
937                         auto_ptr_char type(exts->getAttributeNS(NULL,SHIBT_L(type)));
938                         log.info("building Replay Cache of type %s...",type.get());
939                         m_outer->m_replayCache=IReplayCache::getInstance(type.get(),exts);
940                     }
941                     else {
942                         // OpenSAML default provider.
943                         log.info("building default Replay Cache...");
944                         m_outer->m_replayCache=IReplayCache::getInstance();
945                     }
946                 }
947             }
948         }
949         
950         // Back to the fully dynamic stuff...next up is the Request Mapper.
951         if (conf.isEnabled(ShibTargetConfig::RequestMapper)) {
952             const DOMElement* child=saml::XML::getFirstChildElement(SHIRE,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RequestMapProvider));
953             if (child) {
954                 auto_ptr_char type(child->getAttributeNS(NULL,SHIBT_L(type)));
955                 log.info("building Request Mapper of type %s...",type.get());
956                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),child);
957                 if (plugin) {
958                     IRequestMapper* reqmap=dynamic_cast<IRequestMapper*>(plugin);
959                     if (reqmap)
960                         m_requestMapper=reqmap;
961                     else {
962                         delete plugin;
963                         log.fatal("plugin was not a Request Mapper object");
964                         throw UnsupportedExtensionException("plugin was not a Request Mapper object");
965                     }
966                 }
967             }
968             else {
969                 log.fatal("can't build Request Mapper object, missing conf:RequestMapProvider element?");
970                 throw MalformedException("can't build Request Mapper object, missing conf:RequestMapProvider element?");
971             }
972         }
973         
974         // Now we load any credentials providers.
975         DOMNodeList* nlist;
976         if (conf.isEnabled(ShibTargetConfig::Credentials)) {
977             nlist=ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(
978                 ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(CredentialsProvider)
979                 );
980             for (int i=0; nlist && i<nlist->getLength(); i++) {
981                 auto_ptr_char type(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIBT_L(type)));
982                 log.info("building Credentials provider of type %s...",type.get());
983                 IPlugIn* plugin=shibConf.getPlugMgr().newPlugin(type.get(),static_cast<DOMElement*>(nlist->item(i)));
984                 if (plugin) {
985                     ICredentials* creds=dynamic_cast<ICredentials*>(plugin);
986                     if (creds)
987                         m_creds.push_back(creds);
988                     else {
989                         delete plugin;
990                         log.fatal("plugin was not a Credentials provider");
991                         throw UnsupportedExtensionException("plugin was not a Credentials provider");
992                     }
993                 }
994             }
995         }
996
997         // Load the default application. This actually has a fixed ID of "default". ;-)
998         const DOMElement* app=saml::XML::getFirstChildElement(
999             ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Applications)
1000             );
1001         if (!app) {
1002             log.fatal("can't build default Application object, missing conf:Applications element?");
1003             throw SAMLException("can't build default Application object, missing conf:Applications element?");
1004         }
1005         XMLApplication* defapp=new XMLApplication(m_outer, m_creds, app);
1006         m_appmap[defapp->getId()]=defapp;
1007         
1008         // Load any overrides.
1009         nlist=app->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Application));
1010         for (int i=0; nlist && i<nlist->getLength(); i++) {
1011             XMLApplication* iapp=new XMLApplication(m_outer,m_creds,static_cast<DOMElement*>(nlist->item(i)),defapp);
1012             if (m_appmap.find(iapp->getId())!=m_appmap.end()) {
1013                 log.fatal("found conf:Application element with duplicate Id attribute");
1014                 throw SAMLException("found conf:Application element with duplicate Id attribute");
1015             }
1016             m_appmap[iapp->getId()]=iapp;
1017         }
1018     }
1019     catch (SAMLException& e) {
1020         log.errorStream() << "Error while loading SP configuration: " << e.what() << CategoryStream::ENDLINE;
1021         throw;
1022     }
1023 #ifndef _DEBUG
1024     catch (...) {
1025         log.error("Unexpected error while loading SP configuration");
1026         throw;
1027     }
1028 #endif
1029 }
1030
1031 XMLConfigImpl::~XMLConfigImpl()
1032 {
1033     delete m_requestMapper;
1034     for (map<string,IApplication*>::iterator i=m_appmap.begin(); i!=m_appmap.end(); i++)
1035         delete i->second;
1036     for (vector<ICredentials*>::iterator j=m_creds.begin(); j!=m_creds.end(); j++)
1037         delete (*j);
1038 }