81d5734f1381d8e5f1748485c554494642513cdf
[shibboleth/sp.git] / xmlproviders / XMLMetadata.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 /* XMLMetadata.cpp - a metadata implementation that uses an XML-based registry
51
52    Scott Cantor
53    9/27/02
54
55    $History:$
56 */
57
58 #include "internal.h"
59
60 #include <sys/types.h>
61 #include <sys/stat.h>
62
63 #include <log4cpp/Category.hh>
64 #include <xercesc/util/XMLChar.hpp>
65 #include <xsec/dsig/DSIGTransformC14n.hpp>
66 #include <xsec/dsig/DSIGReference.hpp>
67 #include <xsec/dsig/DSIGTransformList.hpp>
68 #include <xsec/enc/XSECCryptoException.hpp>
69 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
70 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
71 #include <xsec/framework/XSECException.hpp>
72 #include <xsec/framework/XSECProvider.hpp>
73
74 using namespace shibboleth;
75 using namespace saml;
76 using namespace log4cpp;
77 using namespace std;
78
79 namespace {
80
81     class XMLMetadata;
82     class XMLMetadataImpl : public ReloadableXMLFileImpl
83     {
84     public:
85         class ContactPerson : public IContactPerson
86         {
87         public:
88             ContactPerson(const DOMElement* e);
89             ~ContactPerson() {}
90         
91             ContactType getType() const { return m_type; }
92             const char* getCompany() const { return m_company.get(); }
93             const char* getGivenName() const { return m_givenName.get(); }
94             const char* getSurName() const { return m_surName.get(); }
95             Iterator<string> getEmailAddresses() const { return m_emails; }
96             Iterator<string> getTelephoneNumbers() const { return m_phones; }
97             const DOMElement* getElement() const { return m_root; }
98         
99         private:
100             const DOMElement* m_root;
101             ContactType m_type;
102             auto_ptr<char> m_givenName,m_surName,m_company;
103             vector<string> m_emails,m_phones;
104         };
105         
106         class Organization : public IOrganization
107         {
108         public:
109             Organization(const DOMElement* e);
110             ~Organization() {}
111             
112             const char* getName(const char* lang="en") const { return forLang(m_names,lang); }
113             const char* getDisplayName(const char* lang="en") const { return forLang(m_displays,lang); }
114             const char* getURL(const char* lang="en") const { return forLang(m_urls,lang); }
115             const DOMElement* getElement() const { return m_root; }
116         
117         private:
118             const char* forLang(const map<string,string>& m, const char* lang) const {
119                 map<string,string>::const_iterator i=m.find(lang);
120                 return (i==m.end()) ? NULL : i->second.c_str();
121             }
122             const DOMElement* m_root;
123             map<string,string> m_names,m_displays,m_urls;
124         };
125
126         class EntityDescriptor;
127         
128         class EncryptionMethod : public XENCEncryptionMethod
129         {
130         public:
131             EncryptionMethod(const DOMElement* e);
132             ~EncryptionMethod() {}
133             
134             const XMLCh * getAlgorithm(void) const { return m_alg; }
135             const XMLCh * getDigestMethod(void) const { return m_digest; }
136             const XMLCh * getOAEPparams(void) const { return m_params; }
137             int getKeySize(void) const { return m_size; }
138             DOMElement* getElement(void) const { return const_cast<DOMElement*>(m_root); }
139             void setDigestMethod(const XMLCh * method) {throw exception();}
140             void setOAEPparams(const XMLCh * params) {throw exception();}
141             void setKeySize(int size) {throw exception();}
142         
143         private:
144             const DOMElement* m_root;
145             const XMLCh* m_alg;
146             const XMLCh* m_digest;
147             const XMLCh* m_params;
148             int m_size;
149         };
150         
151         class KeyDescriptor : public IKeyDescriptor
152         {
153         public:
154             KeyDescriptor(const DOMElement* e);
155             ~KeyDescriptor();
156             
157             KeyUse getUse() const { return m_use; }
158             DSIGKeyInfoList* getKeyInfo() const { return m_klist; }
159             saml::Iterator<const XENCEncryptionMethod*> getEncryptionMethods() const { return m_methods; }
160             const DOMElement* getElement() const { return m_root; }
161         
162         private:
163             const DOMElement* m_root;
164             KeyUse m_use;
165             mutable DSIGKeyInfoList* m_klist;
166             vector<const XENCEncryptionMethod*> m_methods;
167         };
168         
169         class KeyAuthority : public IKeyAuthority
170         {
171         public:
172             KeyAuthority(const DOMElement* e);
173             ~KeyAuthority();
174             
175             int getVerifyDepth() const { return m_depth; }
176             Iterator<DSIGKeyInfoList*> getKeyInfos() const { return m_klists; }
177         
178         private:
179             int m_depth;
180             vector<DSIGKeyInfoList*> m_klists;
181         };
182         
183         class Role : public virtual IRoleDescriptor
184         {
185         public:
186             Role(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
187             ~Role();
188             
189             // External contract
190             const IEntityDescriptor* getEntityDescriptor() const {return m_provider;}
191             Iterator<const XMLCh*> getProtocolSupportEnumeration() const {return m_protocolEnum;}
192             bool hasSupport(const XMLCh* protocol) const;
193             const char* getErrorURL() const {return (m_errorURL ? m_errorURL : m_provider->getErrorURL());}
194             bool isValid() const {return time(NULL) < m_validUntil;}
195             Iterator<const IKeyDescriptor*> getKeyDescriptors() const {return m_keys;}
196             const IOrganization* getOrganization() const {return m_org ? m_org : m_provider->getOrganization();}
197             Iterator<const IContactPerson*> getContactPersons() const
198                 {return (m_contacts.empty() ? m_provider->getContactPersons() : m_contacts);}
199             const DOMElement* getElement() const {return m_root;}
200         
201         protected:
202             vector<const XMLCh*> m_protocolEnum;
203             vector<const IKeyDescriptor*> m_keys;
204
205         private:
206             const EntityDescriptor* m_provider;
207             const DOMElement* m_root;
208             XMLCh* m_protocolEnumCopy;
209             char* m_errorURL;
210             Organization* m_org;
211             vector<const IContactPerson*> m_contacts;
212             time_t m_validUntil;
213         };
214         
215         class Endpoint : public virtual IEndpoint
216         {
217         public:
218             Endpoint(const DOMElement* e) : m_root(e),
219                 m_binding(e->getAttributeNS(NULL,L(Binding))),
220                 m_location(e->getAttributeNS(NULL,L(Location))),
221                 m_resploc(e->getAttributeNS(NULL,SHIB_L(ResponseLocation))) {}
222             Endpoint(const XMLCh* binding, const XMLCh* loc)
223                 : m_root(NULL), m_binding(binding), m_location(loc), m_resploc(NULL) {}
224             ~Endpoint() {}
225             
226             const XMLCh* getBinding() const { return m_binding; }
227             const XMLCh* getLocation() const { return m_location; }
228             const XMLCh* getResponseLocation() const { return m_resploc; }
229             const DOMElement* getElement() const { return m_root; }
230         
231         private:
232             const DOMElement* m_root;
233             const XMLCh* m_binding;
234             const XMLCh* m_location;
235             const XMLCh* m_resploc;
236         };
237         
238         class IndexedEndpoint : public Endpoint, public virtual IIndexedEndpoint
239         {
240         public:
241             IndexedEndpoint(const DOMElement* e) : Endpoint(e), m_index(XMLString::parseInt(e->getAttributeNS(NULL,SHIB_L(index)))) {}
242             unsigned short getIndex() const {return m_index;}
243             
244         private:
245             unsigned short m_index;
246         };
247         
248         class EndpointManager : public IEndpointManager
249         {
250         public:
251             EndpointManager() : m_soft(NULL), m_hard(NULL) {}
252             ~EndpointManager() {
253                 for (vector<const IEndpoint*>::iterator i=m_endpoints.begin(); i!=m_endpoints.end(); i++)
254                     delete const_cast<IEndpoint*>(*i);
255             }
256             saml::Iterator<const IEndpoint*> getEndpoints() const {return m_endpoints;}
257             const IEndpoint* getDefaultEndpoint() const {
258                 if (m_hard) return m_hard;
259                 if (m_soft) return m_soft;
260                 if (!m_endpoints.empty()) return *(m_endpoints.begin());
261                 return NULL;
262             }
263             const IEndpoint* getEndpointByIndex(unsigned short index) const {
264                 for (vector<const IEndpoint*>::const_iterator i=m_endpoints.begin(); i!=m_endpoints.end(); i++) {
265                     const IIndexedEndpoint* temp=dynamic_cast<const IIndexedEndpoint*>(*i);
266                     if (temp && index==temp->getIndex())
267                         return temp;
268                 }
269                 return NULL;
270             }
271             const IEndpoint* getEndpointByBinding(const XMLCh* binding) const {
272                 for (vector<const IEndpoint*>::const_iterator i=m_endpoints.begin(); i!=m_endpoints.end(); i++)
273                     if (!XMLString::compareString(binding,(*i)->getBinding()))
274                         return *i;
275                 return NULL;
276             }
277             void add(IEndpoint* e) {
278                 m_endpoints.push_back(e);
279                 if (!m_hard && e->getElement()) {
280                     const XMLCh* v=e->getElement()->getAttributeNS(NULL,SHIB_L(isDefault));
281                     if (v && (*v==chDigit_1 || *v==chLatin_t))  // explicit default
282                         m_hard=e;
283                     else if ((!v || !*v) && !m_soft)            // implicit default
284                         m_soft=e;
285                 }
286                 else if (!m_hard && !m_soft) {
287                     // No default yet, so this one qualifies as an implicit.
288                     m_soft=e;
289                 }
290             }
291             
292         private:
293             vector<const IEndpoint*> m_endpoints;
294             const IEndpoint* m_soft;    // Soft default (not explicit)
295             const IEndpoint* m_hard;    // Hard default (explicit)
296         };
297         
298         class SSORole : public Role, public virtual ISSODescriptor
299         {
300         public:
301             SSORole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
302             ~SSORole() {}
303             const IEndpointManager* getArtifactResolutionServiceManager() const {return &m_artifact;}
304             const IEndpointManager* getSingleLogoutServiceManager() const {return &m_logout;}
305             const IEndpointManager* getManageNameIDServiceManager() const {return &m_nameid;}
306             saml::Iterator<const XMLCh*> getNameIDFormats() const {return m_formats;}
307             
308         private:
309             EndpointManager m_artifact,m_logout,m_nameid;
310             vector<const XMLCh*> m_formats;
311         };
312
313         class ScopedRole : public virtual IScopedRoleDescriptor
314         {
315         public:
316             ScopedRole(const DOMElement* e);
317             saml::Iterator<std::pair<const XMLCh*,bool> > getScopes() const {return m_scopes;}
318
319         private:
320             vector<pair<const XMLCh*,bool> > m_scopes;
321         };
322         
323         class IDPRole : public SSORole, public ScopedRole, public virtual IIDPSSODescriptor
324         {
325         public:
326             IDPRole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
327             ~IDPRole();
328             bool getWantAuthnRequestsSigned() const {return m_wantAuthnRequestsSigned;}
329             const IEndpointManager* getSingleSignOnServiceManager() const {return &m_sso;}
330             const IEndpointManager* getNameIDMappingServiceManager() const {return &m_mapping;}
331             const IEndpointManager* getAssertionIDRequestServiceManager() const {return &m_idreq;}
332             saml::Iterator<const XMLCh*> getAttributeProfiles() const {return m_attrprofs;}
333             saml::Iterator<const saml::SAMLAttribute*> getAttributes() const {return m_attrs;}
334         
335         private:
336             EndpointManager m_sso,m_mapping,m_idreq;
337             vector<const XMLCh*> m_attrprofs;
338             vector<const SAMLAttribute*> m_attrs;
339             bool m_wantAuthnRequestsSigned;
340             const XMLCh* m_sourceId;
341             friend class EntityDescriptor;
342         };
343
344         class AARole : public Role, public ScopedRole, public virtual IAttributeAuthorityDescriptor
345         {
346         public:
347             AARole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
348             ~AARole();
349             const IEndpointManager* getAttributeServiceManager() const {return &m_query;}
350             const IEndpointManager* getAssertionIDRequestServiceManager() const {return &m_idreq;}
351             saml::Iterator<const XMLCh*> getNameIDFormats() const {return m_formats;}
352             saml::Iterator<const XMLCh*> getAttributeProfiles() const {return m_attrprofs;}
353             saml::Iterator<const saml::SAMLAttribute*> getAttributes() const {return m_attrs;}
354         
355         private:
356             EndpointManager m_query,m_idreq;
357             vector<const XMLCh*> m_formats,m_attrprofs;
358             vector<const SAMLAttribute*> m_attrs;
359         };
360     
361         class EntityDescriptor : public IExtendedEntityDescriptor
362         {
363         public:
364             EntityDescriptor(
365                 const DOMElement* e,
366                 XMLMetadataImpl* wrapper,
367                 time_t validUntil=LONG_MAX,
368                 const IEntitiesDescriptor* parent=NULL
369                 );
370             ~EntityDescriptor();
371         
372             // External contract
373             const XMLCh* getId() const {return m_id;}
374             bool isValid() const {return time(NULL) < m_validUntil;}
375             Iterator<const IRoleDescriptor*> getRoleDescriptors() const {return m_roles;}
376             const IIDPSSODescriptor* getIDPSSODescriptor(const XMLCh* protocol) const;
377             const ISPSSODescriptor* getSPSSODescriptor(const XMLCh* protocol) const {return NULL;}
378             const IAuthnAuthorityDescriptor* getAuthnAuthorityDescriptor(const XMLCh* protocol) const {return NULL;}
379             const IAttributeAuthorityDescriptor* getAttributeAuthorityDescriptor(const XMLCh* protocol) const;
380             const IPDPDescriptor* getPDPDescriptor(const XMLCh* protocol) const {return NULL;}
381             const IAffiliationDescriptor* getAffiliationDescriptor() const {return NULL;}
382             const IOrganization* getOrganization() const {return m_org;}
383             Iterator<const IContactPerson*> getContactPersons() const {return m_contacts;}
384             Iterator<pair<const XMLCh*,const XMLCh*> > getAdditionalMetadataLocations() const {return m_locs;}
385             const IEntitiesDescriptor* getEntitiesDescriptor() const {return m_parent;}
386             Iterator<const IKeyAuthority*> getKeyAuthorities() const {return m_keyauths;}
387             const DOMElement* getElement() const {return m_root;}
388
389             // Used internally
390             const char* getErrorURL() const {return m_errorURL.get();}
391             time_t getValidUntil() const {return m_validUntil;}
392         private:
393             const DOMElement* m_root;
394             const IEntitiesDescriptor* m_parent;
395             const XMLCh* m_id;
396             auto_ptr<char> m_errorURL;
397             IOrganization* m_org;
398             vector<const IContactPerson*> m_contacts;
399             vector<const IRoleDescriptor*> m_roles;
400             vector<pair<const XMLCh*,const XMLCh*> > m_locs;
401             vector<const IKeyAuthority*> m_keyauths;
402             time_t m_validUntil;
403         };
404
405         class EntitiesDescriptor : public IExtendedEntitiesDescriptor
406         {
407         public:
408             EntitiesDescriptor(
409                 const DOMElement* e,
410                 XMLMetadataImpl* wrapper,
411                 time_t validUntil=LONG_MAX,
412                 const IEntitiesDescriptor* parent=NULL
413                 );
414             ~EntitiesDescriptor();
415             
416             const XMLCh* getName() const {return m_name;}
417             bool isValid() const {return time(NULL) < m_validUntil;}
418             const IEntitiesDescriptor* getEntitiesDescriptor() const {return m_parent;}
419             Iterator<const IEntitiesDescriptor*> getEntitiesDescriptors() const {return m_groups;}
420             Iterator<const IEntityDescriptor*> getEntityDescriptors() const {return m_providers;}
421             Iterator<const IKeyAuthority*> getKeyAuthorities() const {return m_keyauths;}
422             const DOMElement* getElement() const {return m_root;}
423         
424             // Used internally
425             time_t getValidUntil() const {return m_validUntil;}
426         private:
427             const DOMElement* m_root;
428             const IEntitiesDescriptor* m_parent;
429             const XMLCh* m_name;
430             vector<const IEntitiesDescriptor*> m_groups;
431             vector<const IEntityDescriptor*> m_providers;
432             vector<const IKeyAuthority*> m_keyauths;
433             time_t m_validUntil;
434         };
435
436         XMLMetadataImpl(const char* pathname, const XMLMetadata* wrapper)
437             : ReloadableXMLFileImpl(pathname), m_outer(wrapper), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
438         XMLMetadataImpl(const DOMElement* e, const XMLMetadata* wrapper)
439             : ReloadableXMLFileImpl(e), m_outer(wrapper), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
440         void init();
441         ~XMLMetadataImpl();
442
443         typedef multimap<string,const EntityDescriptor*> sitemap_t;
444         typedef multimap<string,const EntitiesDescriptor*> groupmap_t;
445         sitemap_t m_sites;
446         sitemap_t m_sources;
447         groupmap_t m_groups;
448         EntityDescriptor* m_rootProvider;
449         EntitiesDescriptor* m_rootGroup;
450         const XMLMetadata* m_outer;
451     };
452
453     class XMLMetadata : public IMetadata, public ReloadableXMLFile
454     {
455     public:
456         XMLMetadata(const DOMElement* e);
457         ~XMLMetadata() {delete m_credResolver;}
458
459         const IEntityDescriptor* lookup(const char* providerId, bool strict=true) const;
460         const IEntityDescriptor* lookup(const XMLCh* providerId, bool strict=true) const;
461         const IEntityDescriptor* lookup(const saml::SAMLArtifact* artifact) const;
462         const IEntitiesDescriptor* lookupGroup(const char* name, bool strict=true) const;
463         const IEntitiesDescriptor* lookupGroup(const XMLCh* name, bool strict=true) const;
464         pair<const IEntitiesDescriptor*,const IEntityDescriptor*> getRoot() const;
465         
466         bool verifySignature(DOMDocument* doc, const DOMElement* parent, bool failUnsigned) const;
467         
468     protected:
469         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
470         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
471         
472     private:
473         bool m_exclusions,m_verify;
474         set<string> m_set;
475         ICredResolver* m_credResolver;
476     };
477 }
478
479 IPlugIn* XMLMetadataFactory(const DOMElement* e)
480 {
481     auto_ptr<XMLMetadata> m(new XMLMetadata(e));
482     m->getImplementation();
483     return m.release();
484 }
485
486 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const DOMElement* e, bool first) const
487 {
488     return new XMLMetadataImpl(e,this);
489 }
490
491 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const char* pathname, bool first) const
492 {
493     return new XMLMetadataImpl(pathname,this);
494 }
495
496 XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e) : m_root(e)
497 {
498     const XMLCh* type=NULL;
499     
500     // Old metadata or new?
501     if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(Contact))) {
502         type=e->getAttributeNS(NULL,SHIB_L(Type));
503         m_surName=auto_ptr<char>(toUTF8(e->getAttributeNS(NULL,SHIB_L(Name))));
504         if (e->hasAttributeNS(NULL,SHIB_L(Email))) {
505             auto_ptr<char> temp(toUTF8(e->getAttributeNS(NULL,SHIB_L(Email))));
506             if (temp.get())
507                 m_emails.push_back(temp.get());
508         }
509     }
510     else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(ContactPerson))) {
511         type=e->getAttributeNS(NULL,SHIB_L(contactType));
512         DOMNode* n=NULL;
513         e=saml::XML::getFirstChildElement(e);
514         while (e) {
515             if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(Company))) {
516                 n=e->getFirstChild();
517                 if (n) m_company=auto_ptr<char>(toUTF8(n->getNodeValue()));
518             }
519             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(GivenName))) {
520                 n=e->getFirstChild();
521                 if (n) m_givenName=auto_ptr<char>(toUTF8(n->getNodeValue()));
522             }
523             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(SurName))) {
524                 n=e->getFirstChild();
525                 if (n) m_surName=auto_ptr<char>(toUTF8(n->getNodeValue()));
526             }
527             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EmailAddress))) {
528                 n=e->getFirstChild();
529                 if (n) {
530                     auto_ptr<char> temp(toUTF8(n->getNodeValue()));
531                     if (temp.get()) m_emails.push_back(temp.get());
532                 }
533             }
534             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(TelephoneNumber))) {
535                 n=e->getFirstChild();
536                 if (n) {
537                     auto_ptr<char> temp(toUTF8(n->getNodeValue()));
538                     if (temp.get()) m_phones.push_back(temp.get());
539                 }
540             }
541             e=saml::XML::getNextSiblingElement(e);
542         }
543     }
544     
545     if (!XMLString::compareString(type,SHIB_L(technical)))
546         m_type=IContactPerson::technical;
547     else if (!XMLString::compareString(type,SHIB_L(support)))
548         m_type=IContactPerson::support;
549     else if (!XMLString::compareString(type,SHIB_L(administrative)))
550         m_type=IContactPerson::administrative;
551     else if (!XMLString::compareString(type,SHIB_L(billing)))
552         m_type=IContactPerson::billing;
553     else if (!XMLString::compareString(type,SHIB_L(other)))
554         m_type=IContactPerson::other;
555 }
556
557 XMLMetadataImpl::Organization::Organization(const DOMElement* e) : m_root(e)
558 {
559     DOMNode* n=NULL;
560     e=saml::XML::getFirstChildElement(e);
561     while (e) {
562         if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(OrganizationName))) {
563             n=e->getFirstChild();
564             if (n) {
565                 auto_ptr<char> name(toUTF8(n->getNodeValue()));
566                 auto_ptr_char lang(e->getAttributeNS(saml::XML::XML_NS,L(lang)));
567                 m_names[lang.get()]=name.get();
568             }
569         }
570         else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(OrganizationDisplayName))) {
571             n=e->getFirstChild();
572             if (n) {
573                 auto_ptr<char> display(toUTF8(n->getNodeValue()));
574                 auto_ptr_char lang(e->getAttributeNS(saml::XML::XML_NS,L(lang)));
575                 m_displays[lang.get()]=display.get();
576             }
577         }
578         else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(OrganizationURL))) {
579             n=e->getFirstChild();
580             if (n) {
581                 auto_ptr<char> url(toUTF8(n->getNodeValue()));
582                 auto_ptr_char lang(e->getAttributeNS(saml::XML::XML_NS,L(lang)));
583                 m_urls[lang.get()]=url.get();
584             }
585         }
586         e=saml::XML::getNextSiblingElement(e);
587     }
588 }
589
590 XMLMetadataImpl::EncryptionMethod::EncryptionMethod(const DOMElement* e) : m_root(e)
591 {
592     m_alg=e->getAttributeNS(NULL,SHIB_L(Algorithm));
593     e=saml::XML::getFirstChildElement(e);
594     while (e) {
595         if (saml::XML::isElementNamed(e,::XML::XMLENC_NS,SHIB_L(KeySize))) {
596             DOMNode* n=e->getFirstChild();
597             if (n) m_size=XMLString::parseInt(n->getNodeValue());
598         }
599         else if (saml::XML::isElementNamed(e,saml::XML::XMLSIG_NS,SHIB_L(DigestMethod))) {
600             DOMNode* n=e->getFirstChild();
601             if (n) m_digest=n->getNodeValue();
602         }
603         else if (saml::XML::isElementNamed(e,::XML::XMLENC_NS,SHIB_L(OAEParams))) {
604             DOMNode* n=e->getFirstChild();
605             if (n) m_params=n->getNodeValue();
606         }
607         e=saml::XML::getNextSiblingElement(e);
608     }
609 }
610
611 XMLMetadataImpl::KeyDescriptor::KeyDescriptor(const DOMElement* e) : m_root(e), m_use(unspecified), m_klist(NULL)
612 {
613 #ifdef _DEBUG
614     saml::NDC ndc("KeyDescriptor");
615 #endif
616     if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(use)),SHIB_L(encryption)))
617         m_use=encryption;
618     else if (!XMLString::compareString(e->getAttributeNS(NULL,SHIB_L(use)),SHIB_L(signing)))
619         m_use=signing;
620     
621     m_klist = new DSIGKeyInfoList(NULL);
622
623     // Process ds:KeyInfo
624     e=saml::XML::getFirstChildElement(e);
625
626     // We let XMLSec hack through anything it can. This should evolve over time, or we can
627     // plug in our own KeyResolver later...
628     try {
629         if (!m_klist->loadListFromXML(const_cast<DOMElement*>(e)))
630             Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").warn(
631                 "skipping ds:KeyInfo element containing unsupported children"
632                 );
633     }
634     catch (XSECCryptoException& xe) {
635         Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error("unable to process ds:KeyInfo element: %s",xe.getMsg());
636     }
637     
638     // Check for encryption methods.
639     e=saml::XML::getNextSiblingElement(e);
640     while (e && saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EncryptionMethod)))
641         m_methods.push_back(new EncryptionMethod(e));
642 }
643
644 XMLMetadataImpl::KeyDescriptor::~KeyDescriptor()
645 {
646     for (vector<const XENCEncryptionMethod*>::iterator i=m_methods.begin(); i!=m_methods.end(); i++)
647         delete const_cast<XENCEncryptionMethod*>(*i);
648     delete m_klist;
649 }
650
651 XMLMetadataImpl::KeyAuthority::KeyAuthority(const DOMElement* e) : m_depth(1)
652 {
653 #ifdef _DEBUG
654     saml::NDC ndc("KeyAuthority");
655 #endif
656     if (e->hasAttributeNS(NULL,SHIB_L(VerifyDepth)))
657         m_depth=XMLString::parseInt(e->getAttributeNS(NULL,SHIB_L(VerifyDepth)));
658     
659     // Process ds:KeyInfo children
660     e=saml::XML::getFirstChildElement(e,saml::XML::XMLSIG_NS,L(KeyInfo));
661     while (e) {
662         auto_ptr<DSIGKeyInfoList> klist(new DSIGKeyInfoList(NULL));
663
664         // We let XMLSec hack through anything it can. This should evolve over time, or we can
665         // plug in our own KeyResolver later...
666         DOMElement* child=saml::XML::getFirstChildElement(e);
667         while (child) {
668             try {
669                 if (!klist->addXMLKeyInfo(child)) {
670                     Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").warn(
671                         "skipped unresolvable ds:KeyInfo child element");
672                 }
673             }
674             catch (XSECCryptoException& xe) {
675                 Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error(
676                     "unable to process ds:KeyInfo child element: %s",xe.getMsg());
677             }
678             child=saml::XML::getNextSiblingElement(child);
679         }
680         
681         if (klist->getSize()>0)
682             m_klists.push_back(klist.release());
683         else
684             Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").warn(
685                 "skipping ds:KeyInfo with no resolvable child elements");
686         e=saml::XML::getNextSiblingElement(e,saml::XML::XMLSIG_NS,L(KeyInfo));
687     }
688 }
689
690 XMLMetadataImpl::KeyAuthority::~KeyAuthority()
691 {
692     for (vector<DSIGKeyInfoList*>::iterator i=m_klists.begin(); i!=m_klists.end(); i++)
693         delete (*i);
694 }
695
696 XMLMetadataImpl::Role::Role(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
697     : m_provider(provider), m_errorURL(NULL), m_protocolEnumCopy(NULL), m_org(NULL), m_validUntil(validUntil), m_root(e)
698 {
699     // Check the root element namespace. If SAML2, assume it's the std schema.
700     if (e && !XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
701        
702         if (e->hasAttributeNS(NULL,SHIB_L(validUntil))) {
703             SAMLDateTime exp(e->getAttributeNS(NULL,SHIB_L(validUntil)));
704             exp.parseDateTime();
705             m_validUntil=min(m_validUntil,exp.getEpoch());
706         }
707         
708         if (e->hasAttributeNS(NULL,SHIB_L(errorURL)))
709             m_errorURL=toUTF8(e->getAttributeNS(NULL,SHIB_L(errorURL)));
710         
711         // Chop the protocol list into pieces...assume any whitespace can appear in between.
712         m_protocolEnumCopy=XMLString::replicate(e->getAttributeNS(NULL,SHIB_L(protocolSupportEnumeration)));
713         XMLCh* temp=m_protocolEnumCopy;
714         while (temp && *temp) {
715             XMLCh* start=temp++;
716             while (*temp && !XMLChar1_1::isWhitespace(*temp)) temp++;
717             if (*temp)
718                 *temp++=chNull;
719             m_protocolEnum.push_back(start);
720             while (*temp && XMLChar1_1::isWhitespace(*temp)) temp++;
721         }
722         
723         e=saml::XML::getFirstChildElement(m_root,::XML::SAML2META_NS,SHIB_L(KeyDescriptor));
724         while (e) {
725             m_keys.push_back(new KeyDescriptor(e));
726             e=saml::XML::getNextSiblingElement(e,::XML::SAML2META_NS,SHIB_L(KeyDescriptor));
727         }
728
729         e=saml::XML::getFirstChildElement(m_root,::XML::SAML2META_NS,SHIB_L(Organization));
730         if (e)
731             m_org=new Organization(e);
732
733         e=saml::XML::getFirstChildElement(m_root,::XML::SAML2META_NS,SHIB_L(ContactPerson));
734         while (e) {
735             m_contacts.push_back(new ContactPerson(e));
736             e=saml::XML::getNextSiblingElement(e,::XML::SAML2META_NS,SHIB_L(ContactPerson));
737         }
738     }
739 }
740
741 XMLMetadataImpl::Role::~Role()
742 {
743     delete m_org;
744     delete m_errorURL;
745     if (m_protocolEnumCopy) XMLString::release(&m_protocolEnumCopy);
746     for (vector<const IKeyDescriptor*>::iterator i=m_keys.begin(); i!=m_keys.end(); i++)
747         delete const_cast<IKeyDescriptor*>(*i);
748     for (vector<const IContactPerson*>::iterator j=m_contacts.begin(); j!=m_contacts.end(); j++)
749         delete const_cast<IContactPerson*>(*j);
750 }
751
752 bool XMLMetadataImpl::Role::hasSupport(const XMLCh* protocol) const
753 {
754     Iterator<const XMLCh*> i(m_protocolEnum);
755     while (i.hasNext()) {
756         if (!XMLString::compareString(protocol,i.next()))
757             return true;
758     }
759     return false;
760 }
761
762 XMLMetadataImpl::SSORole::SSORole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
763     : Role(provider,validUntil,e)
764 {
765     // Check the root element namespace. If SAML2, assume it's the std schema.
766     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
767         int i;
768         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(ArtifactResolutionService));
769         for (i=0; nlist && i<nlist->getLength(); i++)
770             m_artifact.add(new IndexedEndpoint(static_cast<DOMElement*>(nlist->item(i))));
771
772         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(SingleLogoutService));
773         for (i=0; nlist && i<nlist->getLength(); i++)
774             m_logout.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
775
776         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(ManageNameIDService));
777         for (i=0; nlist && i<nlist->getLength(); i++)
778             m_nameid.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
779
780         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(NameIDFormat));
781         for (i=0; nlist && i<nlist->getLength(); i++) {
782             DOMNode* n=nlist->item(i)->getFirstChild();
783             if (n) m_formats.push_back(n->getNodeValue());
784         }
785     }
786     else {
787         // For old style, we just do SAML 1.1 compatibility with Shib handles.
788         m_protocolEnum.push_back(saml::XML::SAML11_PROTOCOL_ENUM);
789         m_formats.push_back(shibboleth::Constants::SHIB_NAMEID_FORMAT_URI);
790     }
791 }
792
793 XMLMetadataImpl::ScopedRole::ScopedRole(const DOMElement* e)
794 {
795     // Check the root element namespace. If SAML2, assume it's the std schema.
796     DOMNodeList* nlist=NULL;
797     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
798         e=saml::XML::getFirstChildElement(e,::XML::SAML2META_NS,SHIB_L(Extensions));
799         if (e) nlist=e->getElementsByTagNameNS(::XML::SHIBMETA_NS,SHIB_L(Scope));
800     }
801     else {
802         nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Domain));
803     }
804     
805     for (int i=0; nlist && i < nlist->getLength(); i++) {
806         const XMLCh* dom=(nlist->item(i)->hasChildNodes()) ? nlist->item(i)->getFirstChild()->getNodeValue() : NULL;
807         if (dom && *dom) {
808             const XMLCh* regexp=static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(regexp));
809             m_scopes.push_back(
810                 pair<const XMLCh*,bool>(dom,(regexp && (*regexp==chLatin_t || *regexp==chDigit_1)))
811                 );
812         }
813     }
814 }
815
816 XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
817     : SSORole(provider,validUntil,e), ScopedRole(e), m_wantAuthnRequestsSigned(false), m_sourceId(NULL)
818 {
819     // Check the root element namespace. If SAML2, assume it's the std schema.
820     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
821         const XMLCh* flag=e->getAttributeNS(NULL,SHIB_L(WantAuthnRequestsSigned));
822         m_wantAuthnRequestsSigned=(flag && (*flag==chDigit_1 || *flag==chLatin_t));
823         
824         // Check for SourceID extension.
825         DOMElement* ext=saml::XML::getFirstChildElement(e,::XML::SAML2META_NS,SHIB_L(Extensions));
826         if (ext) {
827             ext=saml::XML::getFirstChildElement(ext,saml::XML::SAML_ARTIFACT_SOURCEID,SHIB_L(SourceID));
828             if (ext && ext->hasChildNodes())
829                 m_sourceId=ext->getFirstChild()->getNodeValue();
830         }
831         
832         int i;
833         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(SingleSignOnService));
834         for (i=0; nlist && i<nlist->getLength(); i++)
835             m_sso.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
836
837         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(NameIDMappingService));
838         for (i=0; nlist && i<nlist->getLength(); i++)
839             m_mapping.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
840
841         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AssertionIDRequestService));
842         for (i=0; nlist && i<nlist->getLength(); i++)
843             m_idreq.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
844
845         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AttributeProfile));
846         for (i=0; nlist && i<nlist->getLength(); i++) {
847             DOMNode* n=nlist->item(i)->getFirstChild();
848             if (n) m_attrprofs.push_back(n->getNodeValue());
849         }
850
851         nlist=e->getElementsByTagNameNS(::XML::SAML2ASSERT_NS,L(Attribute));
852         for (i=0; nlist && i<nlist->getLength(); i++) {
853             // For now, we need to convert these to plain SAML 1.1 attributes.
854             DOMElement* src=static_cast<DOMElement*>(nlist->item(i));
855             DOMElement* copy=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(Attribute));
856             copy->setAttributeNS(NULL,L(AttributeName),src->getAttributeNS(NULL,SHIB_L(Name)));
857             copy->setAttributeNS(NULL,L(AttributeNamespace),src->getAttributeNS(NULL,SHIB_L(NameFormat)));
858             src=saml::XML::getFirstChildElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
859             while (src) {
860                 src=saml::XML::getNextSiblingElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
861                 DOMElement* val=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(AttributeValue));
862                 DOMNamedNodeMap* attrs = src->getAttributes();
863                 for (int j=0; j<attrs->getLength(); j++)
864                     val->setAttributeNodeNS(static_cast<DOMAttr*>(e->getOwnerDocument()->importNode(attrs->item(j),true)));
865                 while (src->hasChildNodes())
866                     val->appendChild(src->getFirstChild());
867                 copy->appendChild(val);
868             }
869             m_attrs.push_back(SAMLAttribute::getInstance(copy));
870         }
871     }
872     else {
873         m_protocolEnum.push_back(Constants::SHIB_NS);
874         m_attrprofs.push_back(Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
875         int i;
876         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(HandleService));
877         for (i=0; nlist && i<nlist->getLength(); i++) {
878             // Manufacture an endpoint for the "Shib" binding.
879             m_sso.add(
880                 new Endpoint(Constants::SHIB_AUTHNREQUEST_PROFILE_URI,static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,L(Location)))
881                 );
882
883             // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
884             DOMElement* kd=e->getOwnerDocument()->createElementNS(::XML::SAML2META_NS,SHIB_L(KeyDescriptor));
885             DOMElement* ki=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,L(KeyInfo));
886             DOMElement* kn=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
887             kn->appendChild(
888                 e->getOwnerDocument()->createTextNode(
889                     static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(Name))
890                     )
891                 );
892             ki->appendChild(kn);
893             kd->appendChild(ki);
894             kd->setAttributeNS(NULL,SHIB_L(use),SHIB_L(signing));
895             m_keys.push_back(new KeyDescriptor(kd));
896         }
897     }
898 }
899
900 XMLMetadataImpl::IDPRole::~IDPRole()
901 {
902     for (vector<const SAMLAttribute*>::iterator i=m_attrs.begin(); i!=m_attrs.end(); i++)
903         delete const_cast<SAMLAttribute*>(*i);
904 }
905
906 XMLMetadataImpl::AARole::AARole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
907     : Role(provider,validUntil,e), ScopedRole(e)
908 {
909     // Check the root element namespace. If SAML2, assume it's the std schema.
910     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
911         int i;
912         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AttributeService));
913         for (i=0; nlist && i<nlist->getLength(); i++)
914             m_query.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
915
916         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AssertionIDRequestService));
917         for (i=0; nlist && i<nlist->getLength(); i++)
918             m_idreq.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
919
920         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(NameIDFormat));
921         for (i=0; nlist && i<nlist->getLength(); i++) {
922             DOMNode* n=nlist->item(i)->getFirstChild();
923             if (n) m_formats.push_back(n->getNodeValue());
924         }
925
926         nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AttributeProfile));
927         for (i=0; nlist && i<nlist->getLength(); i++) {
928             DOMNode* n=nlist->item(i)->getFirstChild();
929             if (n) m_attrprofs.push_back(n->getNodeValue());
930         }
931
932         nlist=e->getElementsByTagNameNS(::XML::SAML2ASSERT_NS,L(Attribute));
933         for (i=0; nlist && i<nlist->getLength(); i++) {
934             // For now, we need to convert these to plain SAML 1.1 attributes.
935             DOMElement* src=static_cast<DOMElement*>(nlist->item(i));
936             DOMElement* copy=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(Attribute));
937             copy->setAttributeNS(NULL,L(AttributeName),src->getAttributeNS(NULL,SHIB_L(Name)));
938             copy->setAttributeNS(NULL,L(AttributeNamespace),src->getAttributeNS(NULL,SHIB_L(NameFormat)));
939             src=saml::XML::getFirstChildElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
940             while (src) {
941                 src=saml::XML::getNextSiblingElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
942                 DOMElement* val=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(AttributeValue));
943                 DOMNamedNodeMap* attrs = src->getAttributes();
944                 for (int j=0; j<attrs->getLength(); j++)
945                     val->setAttributeNodeNS(static_cast<DOMAttr*>(e->getOwnerDocument()->importNode(attrs->item(j),true)));
946                 while (src->hasChildNodes())
947                     val->appendChild(src->getFirstChild());
948                 copy->appendChild(val);
949             }
950             m_attrs.push_back(SAMLAttribute::getInstance(copy));
951         }
952     }
953     else {
954         // For old style, we just do SAML 1.1 compatibility with Shib handles.
955         m_protocolEnum.push_back(saml::XML::SAML11_PROTOCOL_ENUM);
956         m_formats.push_back(Constants::SHIB_NAMEID_FORMAT_URI);
957         m_attrprofs.push_back(Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
958         int i;
959         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AttributeAuthority));
960         for (i=0; nlist && i<nlist->getLength(); i++) {
961             // Manufacture an endpoint for the SOAP binding.
962             m_query.add(
963                 new Endpoint(
964                     SAMLBinding::SOAP,
965                     static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,L(Location))
966                     )
967                 );
968
969             // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
970             DOMElement* kd=e->getOwnerDocument()->createElementNS(::XML::SAML2META_NS,SHIB_L(KeyDescriptor));
971             DOMElement* ki=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,L(KeyInfo));
972             DOMElement* kn=e->getOwnerDocument()->createElementNS(saml::XML::XMLSIG_NS,SHIB_L(KeyName));
973             kn->appendChild(
974                 e->getOwnerDocument()->createTextNode(
975                     static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(Name))
976                     )
977                 );
978             ki->appendChild(kn);
979             kd->appendChild(ki);
980             m_keys.push_back(new KeyDescriptor(kd));
981         }
982     }
983 }
984
985 XMLMetadataImpl::AARole::~AARole()
986 {
987     for (vector<const SAMLAttribute*>::iterator i=m_attrs.begin(); i!=m_attrs.end(); i++)
988         delete const_cast<SAMLAttribute*>(*i);
989 }
990
991 XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
992     const DOMElement* e, XMLMetadataImpl* wrapper, time_t validUntil, const IEntitiesDescriptor* parent
993     ) : m_root(e), m_parent(parent), m_org(NULL), m_validUntil(validUntil)
994 {
995     // Check the root element namespace. If SAML2, assume it's the std schema.
996     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
997         m_id=e->getAttributeNS(NULL,SHIB_L(entityID));
998
999         if (e->hasAttributeNS(NULL,SHIB_L(validUntil))) {
1000             SAMLDateTime exp(e->getAttributeNS(NULL,SHIB_L(validUntil)));
1001             exp.parseDateTime();
1002             m_validUntil=min(validUntil,exp.getEpoch());
1003         }
1004
1005         DOMElement* child=saml::XML::getFirstChildElement(e);
1006         while (child) {
1007             // Process the various kinds of children that we care about...
1008             if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(Extensions))) {
1009                 DOMElement* ext = saml::XML::getFirstChildElement(child,::XML::SHIBMETA_NS,SHIB_L(KeyAuthority));
1010                 while (ext) {
1011                     m_keyauths.push_back(new KeyAuthority(ext));
1012                     ext = saml::XML::getNextSiblingElement(ext,::XML::SHIBMETA_NS,SHIB_L(KeyAuthority));
1013                 }
1014             }
1015             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(ContactPerson))) {
1016                 m_contacts.push_back(new ContactPerson(child));
1017             }
1018             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(Organization))) {
1019                 m_org=new Organization(child);
1020             }
1021             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(AdditionalMetadataLocation))) {
1022                 DOMNode* loc=child->getFirstChild();
1023                 if (loc)
1024                     m_locs.push_back(
1025                     pair<const XMLCh*,const XMLCh*>(child->getAttributeNS(NULL,::XML::Literals::_namespace),loc->getNodeValue())
1026                         );
1027             }
1028             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(IDPSSODescriptor))) {
1029                 if (wrapper->m_outer->verifySignature(child->getOwnerDocument(),child,false))
1030                     m_roles.push_back(new IDPRole(this,m_validUntil,child));
1031             }
1032             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(AttributeAuthorityDescriptor))) {
1033                 if (wrapper->m_outer->verifySignature(child->getOwnerDocument(),child,false))
1034                     m_roles.push_back(new AARole(this,m_validUntil,child));
1035             }
1036             child = saml::XML::getNextSiblingElement(child);
1037         }
1038     }
1039     else {
1040         m_id=e->getAttributeNS(NULL,SHIB_L(Name));
1041         m_errorURL=auto_ptr<char>(toUTF8(e->getAttributeNS(NULL,SHIB_L(ErrorURL))));
1042         
1043         bool idp=false,aa=false;    // only want to build a role once
1044         DOMElement* child=saml::XML::getFirstChildElement(e);
1045         while (child) {
1046             // Process the various kinds of OriginSite children that we care about...
1047             if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(Contact))) {
1048                 m_contacts.push_back(new ContactPerson(child));
1049             }
1050             else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(HandleService)) && !idp) {
1051                 // Create the IDP role if needed.
1052                 m_roles.push_back(new IDPRole(this, m_validUntil, e));
1053                 idp=true;
1054             }
1055             else if (saml::XML::isElementNamed(child,::XML::SHIB_NS,SHIB_L(AttributeAuthority)) && !aa) {
1056                 // Create the AA role if needed.
1057                 m_roles.push_back(new AARole(this, m_validUntil, e));
1058                 aa=true;
1059             }
1060             child = saml::XML::getNextSiblingElement(child);
1061         }
1062     }
1063
1064     auto_ptr_char id(m_id);
1065     wrapper->m_sites.insert(pair<const string,const EntityDescriptor*>(id.get(),this));
1066     
1067     // Look for an IdP role, and register the artifact source ID and endpoints.
1068     const IDPRole* idp=NULL;
1069     for (vector<const IRoleDescriptor*>::const_iterator r=m_roles.begin(); r!=m_roles.end(); r++) {
1070         if (idp=dynamic_cast<const IDPRole*>(*r)) {
1071             if (idp->m_sourceId) {
1072                 auto_ptr_char sourceid(idp->m_sourceId);
1073                 wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(sourceid.get(),this));
1074             }
1075             else {
1076                 string sourceid=SAMLArtifact::toHex(SAMLArtifactType0001::generateSourceId(id.get()));
1077                 wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(sourceid,this));
1078             }
1079             Iterator<const IEndpoint*> locs=idp->getArtifactResolutionServiceManager()->getEndpoints();
1080             while (locs.hasNext()) {
1081                 auto_ptr_char loc(locs.next()->getLocation());
1082                 wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(loc.get(),this));
1083             }
1084         }
1085     }
1086 }
1087
1088 const IIDPSSODescriptor* XMLMetadataImpl::EntityDescriptor::getIDPSSODescriptor(const XMLCh* protocol) const
1089 {
1090     const IIDPSSODescriptor* ret=NULL;
1091     for (vector<const IRoleDescriptor*>::const_iterator i=m_roles.begin(); i!=m_roles.end(); i++) {
1092         if ((*i)->hasSupport(protocol) && (*i)->isValid() && (ret=dynamic_cast<const IIDPSSODescriptor*>(*i)))
1093             return ret;
1094     }
1095     return NULL;
1096 }
1097
1098 const IAttributeAuthorityDescriptor* XMLMetadataImpl::EntityDescriptor::getAttributeAuthorityDescriptor(const XMLCh* protocol) const
1099 {
1100     const IAttributeAuthorityDescriptor* ret=NULL;
1101     for (vector<const IRoleDescriptor*>::const_iterator i=m_roles.begin(); i!=m_roles.end(); i++) {
1102         if ((*i)->hasSupport(protocol) && (*i)->isValid() && (ret=dynamic_cast<const IAttributeAuthorityDescriptor*>(*i)))
1103             return ret;
1104     }
1105     return NULL;
1106 }
1107
1108 XMLMetadataImpl::EntityDescriptor::~EntityDescriptor()
1109 {
1110     delete m_org;
1111     for (vector<const IContactPerson*>::iterator i=m_contacts.begin(); i!=m_contacts.end(); i++)
1112         delete const_cast<IContactPerson*>(*i);
1113     for (vector<const IRoleDescriptor*>::iterator j=m_roles.begin(); j!=m_roles.end(); j++)
1114         delete const_cast<IRoleDescriptor*>(*j);
1115     for (vector<const IKeyAuthority*>::iterator k=m_keyauths.begin(); k!=m_keyauths.end(); k++)
1116         delete const_cast<IKeyAuthority*>(*k);
1117 }
1118
1119 XMLMetadataImpl::EntitiesDescriptor::EntitiesDescriptor(
1120     const DOMElement* e, XMLMetadataImpl* wrapper, time_t validUntil, const IEntitiesDescriptor* parent
1121     ) : m_root(e), m_name(e->getAttributeNS(NULL,SHIB_L(Name))), m_parent(parent), m_validUntil(validUntil)
1122 {
1123     // Check the root element namespace. If SAML2, assume it's the std schema.
1124     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
1125
1126         if (e->hasAttributeNS(NULL,SHIB_L(validUntil))) {
1127             SAMLDateTime exp(e->getAttributeNS(NULL,SHIB_L(validUntil)));
1128             exp.parseDateTime();
1129             m_validUntil=min(validUntil,exp.getEpoch());
1130         }
1131
1132         e=saml::XML::getFirstChildElement(e);
1133         while (e) {
1134             if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(Extensions))) {
1135                 DOMElement* ext = saml::XML::getFirstChildElement(e,::XML::SHIBMETA_NS,SHIB_L(KeyAuthority));
1136                 while (ext) {
1137                     m_keyauths.push_back(new KeyAuthority(ext));
1138                     ext = saml::XML::getNextSiblingElement(ext,::XML::SHIBMETA_NS,SHIB_L(KeyAuthority));
1139                 }
1140             }
1141             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor))) {
1142                 if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
1143                     m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
1144             }
1145             else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntityDescriptor))) {
1146                 if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
1147                     m_providers.push_back(new EntityDescriptor(e,wrapper,m_validUntil,this));
1148             }
1149             e=saml::XML::getNextSiblingElement(e);
1150         }
1151     }
1152     else {
1153         e=saml::XML::getFirstChildElement(e);
1154         while (e) {
1155             if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
1156                 if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
1157                     m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
1158             }
1159             else if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(OriginSite)))
1160                 m_providers.push_back(new EntityDescriptor(e,wrapper,m_validUntil,this));
1161             e=saml::XML::getNextSiblingElement(e);
1162         }
1163     }
1164
1165     if (!saml::XML::isEmpty(m_name)) {
1166         auto_ptr_char n(m_name);
1167         wrapper->m_groups.insert(pair<const string,const EntitiesDescriptor*>(n.get(),this));
1168     }
1169     else
1170         m_name=NULL;
1171 }
1172
1173 XMLMetadataImpl::EntitiesDescriptor::~EntitiesDescriptor()
1174 {
1175     for (vector<const IEntityDescriptor*>::iterator i=m_providers.begin(); i!=m_providers.end(); i++)
1176         delete const_cast<IEntityDescriptor*>(*i);
1177     for (vector<const IEntitiesDescriptor*>::iterator j=m_groups.begin(); j!=m_groups.end(); j++)
1178         delete const_cast<IEntitiesDescriptor*>(*j);
1179     for (vector<const IKeyAuthority*>::iterator k=m_keyauths.begin(); k!=m_keyauths.end(); k++)
1180         delete const_cast<IKeyAuthority*>(*k);
1181 }
1182
1183 void XMLMetadataImpl::init()
1184 {
1185 #ifdef _DEBUG
1186     NDC ndc("init");
1187 #endif
1188     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata");
1189
1190     try
1191     {
1192         if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor))) {
1193             if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
1194                 m_rootGroup=new EntitiesDescriptor(m_root,this);
1195         }
1196         else if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntityDescriptor))) {
1197             if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
1198                 m_rootProvider=new EntityDescriptor(m_root,this);
1199         }
1200         else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
1201             if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
1202                 m_rootGroup=new EntitiesDescriptor(m_root,this);
1203         }
1204         else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(OriginSite))) {
1205             if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
1206                 m_rootProvider=new EntityDescriptor(m_root,this);
1207         }
1208         else {
1209             log.error("Construction requires a valid SAML metadata file");
1210             throw MetadataException("Construction requires a valid SAML metadata file");
1211         }
1212     }
1213     catch (SAMLException& e)
1214     {
1215         log.errorStream() << "Error while parsing SAML metadata: " << e.what() << CategoryStream::ENDLINE;
1216         this->~XMLMetadataImpl();
1217         throw;
1218     }
1219 #ifndef _DEBUG
1220     catch (...)
1221     {
1222         log.error("Unexpected error while parsing SAML metadata");
1223         this->~XMLMetadataImpl();
1224         throw;
1225     }
1226 #endif
1227
1228     if (!m_rootGroup && !m_rootProvider) {
1229         log.error("Metadata file contained no valid information");
1230         throw MetadataException("Metadata file contained no valid information");
1231     }
1232 }
1233
1234 XMLMetadataImpl::~XMLMetadataImpl()
1235 {
1236     delete m_rootGroup;
1237     delete m_rootProvider;
1238 }
1239
1240 XMLMetadata::XMLMetadata(const DOMElement* e) : ReloadableXMLFile(e), m_exclusions(true), m_verify(false), m_credResolver(NULL)
1241 {
1242     static const XMLCh uri[] = { chLatin_u, chLatin_r, chLatin_i, chNull };
1243     if (e->hasAttributeNS(NULL,uri)) {
1244         // First check for explicit enablement of entities.
1245         DOMNodeList* nlist=e->getElementsByTagName(SHIB_L(Include));
1246         for (int i=0; nlist && i<nlist->getLength(); i++) {
1247             if (nlist->item(i)->hasChildNodes()) {
1248                 auto_ptr_char temp(nlist->item(i)->getFirstChild()->getNodeValue());
1249                 if (temp.get()) {
1250                     m_set.insert(temp.get());
1251                     m_exclusions=false;
1252                 }
1253             }
1254         }
1255         // If there was no explicit enablement, build a set of exclusions.
1256         if (m_exclusions) {
1257             nlist=e->getElementsByTagName(SHIB_L(Exclude));
1258             for (int j=0; nlist && j<nlist->getLength(); j++) {
1259                 if (nlist->item(j)->hasChildNodes()) {
1260                     auto_ptr_char temp(nlist->item(j)->getFirstChild()->getNodeValue());
1261                     if (temp.get())
1262                         m_set.insert(temp.get());
1263                 }
1264             }
1265         }
1266     }
1267     
1268     const XMLCh* v=e->getAttributeNS(NULL,SHIB_L(verify));
1269     m_verify=(v && (*v==chLatin_t || *v==chDigit_1));
1270
1271     string cr_type;
1272     DOMElement* r=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(FileResolver));
1273     if (r)
1274         cr_type="edu.internet2.middleware.shibboleth.common.Credentials.FileCredentialResolver";            
1275     else {
1276         r=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(CustomResolver));
1277         if (r) {
1278             auto_ptr_char c(r->getAttributeNS(NULL,SHIB_L(Class)));
1279             cr_type=c.get();
1280         }
1281     }
1282     
1283     if (!cr_type.empty()) {
1284         try {
1285             IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(cr_type.c_str(),r);
1286             ICredResolver* cr=dynamic_cast<ICredResolver*>(plugin);
1287             if (cr)
1288                 m_credResolver=cr;
1289             else {
1290                 Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error("plugin was not a credential resolver");
1291                 delete plugin;
1292                 throw UnsupportedExtensionException("plugin was not a credential resolver");
1293             }
1294         }
1295         catch (SAMLException& e) {
1296             Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error("failed to instantiate credential resolver: %s", e.what());
1297             throw;
1298         }
1299     }
1300     
1301     if (m_verify && !m_credResolver) {
1302         delete m_credResolver;
1303         throw MalformedException("Metadata provider told to verify signatures, but a verification key is not available.");
1304     }
1305 }
1306
1307 bool XMLMetadata::verifySignature(DOMDocument* doc, const DOMElement* parent, bool failUnsigned) const
1308 {
1309     if (!m_verify)
1310         return true;
1311
1312 #ifdef _DEBUG
1313     saml::NDC ndc("verifySignature");
1314 #endif
1315     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata");
1316     
1317     DOMElement* sigNode=saml::XML::getFirstChildElement(parent,saml::XML::XMLSIG_NS,L(Signature));
1318     if (!sigNode) {
1319         if (failUnsigned) {
1320             log.error("rejecting unsigned element");
1321             return false;
1322         }
1323         return true;
1324     }
1325     
1326     XSECCryptoX509* cert=NULL;
1327     Iterator<XSECCryptoX509*> certs=m_credResolver->getCertificates();
1328     if (certs.hasNext())
1329         cert=certs.next();
1330     else {
1331         log.error("unable to find any certificates to use in verifying signature");
1332         return false;
1333     }
1334
1335     static const XMLCh ID[]={chLatin_I, chLatin_D, chNull};
1336     static const XMLCh null[]={chDoubleQuote, chDoubleQuote, chNull};
1337
1338     // Load the signature.
1339     XSECProvider prov;
1340     DSIGSignature* sig=NULL;
1341     try {
1342         sig=prov.newSignatureFromDOM(doc,sigNode);
1343         sig->load();
1344
1345         bool valid=false;
1346         const XMLCh* URI=NULL;
1347
1348         // Verify the signature coverage.
1349         DSIGReferenceList* refs=sig->getReferenceList();
1350         if (sig->getSignatureMethod()==SIGNATURE_RSA && refs && refs->getSize()==1) {
1351             DSIGReference* ref=refs->item(0);
1352             if (ref) {
1353                 URI=ref->getURI();
1354                 if (!URI || !*URI || (*URI==chPound &&
1355                         !XMLString::compareString(&URI[1],static_cast<DOMElement*>(sigNode->getParentNode())->getAttributeNS(NULL,ID)))) {
1356                     DSIGTransformList* tlist=ref->getTransforms();
1357                     for (int i=0; tlist && i<tlist->getSize(); i++) {
1358                         if (tlist->item(i)->getTransformType()==TRANSFORM_ENVELOPED_SIGNATURE)
1359                             valid=true;
1360                         else if (tlist->item(i)->getTransformType()!=TRANSFORM_EXC_C14N &&
1361                                  tlist->item(i)->getTransformType()!=TRANSFORM_C14N) {
1362                             valid=false;
1363                             break;
1364                         }
1365                     }
1366                 }
1367             }
1368         }
1369     
1370         if (!valid) {
1371             auto_ptr_char temp((URI && *URI) ? URI : null);
1372             log.error("detected an invalid signature profile (Reference URI was %s)",temp.get());
1373             return false;
1374         }
1375         
1376         sig->setSigningKey(cert->clonePublicKey());
1377         if (!sig->verify()) {
1378             auto_ptr_char temp((URI && *URI) ? URI : null);
1379             log.error("detected an invalid signature value (Reference URI was %s)",temp.get());
1380             return false;
1381         }
1382         
1383         prov.releaseSignature(sig);
1384     }
1385     catch(XSECException& e) {
1386         auto_ptr_char msg(e.getMsg());
1387         log.errorStream() << "caught XMLSec exception while verifying metadata signature: " << msg.get() << CategoryStream::ENDLINE;
1388         if (sig)
1389             prov.releaseSignature(sig);
1390         return false;
1391     }
1392     catch(XSECCryptoException& e) {
1393         log.errorStream() << "caught XMLSecCrypto exception while verifying metadata signature: " << e.getMsg() << CategoryStream::ENDLINE;
1394         if (sig)
1395             prov.releaseSignature(sig);
1396         return false;
1397     }
1398     catch(...) {
1399         if (sig)
1400             prov.releaseSignature(sig);
1401         log.error("caught unknown exception while verifying metadata signature");
1402         throw;
1403     }
1404     return true;
1405 }
1406
1407 const IEntityDescriptor* XMLMetadata::lookup(const char* providerId, bool strict) const
1408 {
1409     if (strict && m_exclusions && m_set.find(providerId)!=m_set.end())
1410         return NULL;
1411     else if (strict && !m_exclusions && m_set.find(providerId)==m_set.end())
1412         return NULL;
1413         
1414     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
1415     pair<XMLMetadataImpl::sitemap_t::iterator,XMLMetadataImpl::sitemap_t::iterator> range=
1416         impl->m_sites.equal_range(providerId);
1417
1418     time_t now=time(NULL);
1419     for (XMLMetadataImpl::sitemap_t::const_iterator i=range.first; i!=range.second; i++)
1420         if (now < i->second->getValidUntil())
1421             return i->second;
1422     
1423     if (!strict && range.first!=range.second)
1424         return range.first->second;
1425         
1426     return NULL;
1427 }
1428
1429 const IEntityDescriptor* XMLMetadata::lookup(const XMLCh* providerId, bool strict) const
1430 {
1431     auto_ptr_char temp(providerId);
1432     return lookup(temp.get(),strict);
1433 }
1434
1435 const IEntityDescriptor* XMLMetadata::lookup(const SAMLArtifact* artifact) const
1436 {
1437     time_t now=time(NULL);
1438     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
1439     pair<XMLMetadataImpl::sitemap_t::iterator,XMLMetadataImpl::sitemap_t::iterator> range;
1440     
1441     // Depends on type of artifact.
1442     const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
1443     if (type1) {
1444         range=impl->m_sources.equal_range(SAMLArtifact::toHex(type1->getSourceID()));
1445     }
1446     else {
1447         const SAMLArtifactType0002* type2=dynamic_cast<const SAMLArtifactType0002*>(artifact);
1448         if (type2) {
1449             range=impl->m_sources.equal_range(type2->getSourceLocation());
1450         }
1451         else
1452             return NULL;
1453     }
1454
1455     // Check exclude list.
1456     if (range.first!=range.second) {
1457         auto_ptr_char id(range.first->second->getId());
1458         if (m_exclusions && m_set.find(id.get())!=m_set.end())
1459             return NULL;
1460         else if (!m_exclusions && m_set.find(id.get())==m_set.end())
1461             return NULL;
1462
1463         for (XMLMetadataImpl::sitemap_t::iterator i=range.first; i!=range.second; i++)
1464             if (now < i->second->getValidUntil())
1465                 return i->second;
1466     }
1467     
1468     return NULL;
1469 }
1470
1471 const IEntitiesDescriptor* XMLMetadata::lookupGroup(const char* name, bool strict) const
1472 {
1473     if (strict && m_exclusions && m_set.find(name)!=m_set.end())
1474         return NULL;
1475     else if (strict && !m_exclusions && m_set.find(name)==m_set.end())
1476         return NULL;
1477         
1478     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
1479     pair<XMLMetadataImpl::groupmap_t::iterator,XMLMetadataImpl::groupmap_t::iterator> range=
1480         impl->m_groups.equal_range(name);
1481
1482     time_t now=time(NULL);
1483     for (XMLMetadataImpl::groupmap_t::iterator i=range.first; i!=range.second; i++)
1484         if (now < i->second->getValidUntil())
1485             return i->second;
1486     
1487     if (!strict && range.first!=range.second)
1488         return range.first->second;
1489         
1490     return NULL;
1491 }
1492
1493 const IEntitiesDescriptor* XMLMetadata::lookupGroup(const XMLCh* name, bool strict) const
1494 {
1495     auto_ptr_char temp(name);
1496     return lookupGroup(temp.get(),strict);
1497 }
1498
1499 pair<const IEntitiesDescriptor*,const IEntityDescriptor*> XMLMetadata::getRoot() const
1500 {
1501     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
1502     return pair<const IEntitiesDescriptor*,const IEntityDescriptor*>(impl->m_rootGroup,impl->m_rootProvider);
1503 }
1504