Had to add Shib prototol support for older metadata.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / metadata / provider / XMLMetadataProvider.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.metadata.provider;
18
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.security.NoSuchAlgorithmException;
22 import java.text.ParseException;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.TimeZone;
31
32 import javax.xml.namespace.QName;
33
34 import org.apache.log4j.Logger;
35 import org.apache.xml.security.encryption.EncryptionMethod;
36 import org.apache.xml.security.exceptions.XMLSecurityException;
37 import org.apache.xml.security.keys.KeyInfo;
38 import org.bouncycastle.util.encoders.Hex;
39 import org.opensaml.SAMLAttribute;
40 import org.opensaml.SAMLBinding;
41 import org.opensaml.SAMLBrowserProfile;
42 import org.opensaml.SAMLException;
43 import org.opensaml.XML;
44 import org.opensaml.artifact.Artifact;
45 import org.opensaml.artifact.SAMLArtifactType0001;
46 import org.opensaml.artifact.SAMLArtifactType0002;
47 import org.opensaml.artifact.Util;
48 import org.w3c.dom.Attr;
49 import org.w3c.dom.Element;
50 import org.w3c.dom.NamedNodeMap;
51 import org.w3c.dom.Node;
52 import org.w3c.dom.NodeList;
53
54 import edu.internet2.middleware.shibboleth.common.Constants;
55 import edu.internet2.middleware.shibboleth.metadata.*;
56
57 /**
58  * @author Scott Cantor
59  */
60 public class XMLMetadataProvider implements Metadata {
61
62         private static Logger log = Logger.getLogger(XMLMetadataProvider.class.getName());
63         private Map     /* <String,ArrayList<EntityDescriptor> > */ sites = new HashMap();
64     private Map /* <String,ArrayList<EntityDescriptor> > */ sources = new HashMap();
65     private XMLEntityDescriptor rootProvider = null;
66     private XMLEntitiesDescriptor rootGroup = null;
67
68         public XMLMetadataProvider(Element e) throws SAMLException {
69         if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntitiesDescriptor"))
70             rootGroup=new XMLEntitiesDescriptor(e,this, Long.MAX_VALUE, null);
71         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntityDescriptor"))
72             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
73         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"SiteGroup"))
74             rootGroup=new XMLEntitiesDescriptor(e,this, Long.MAX_VALUE, null);
75         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"OriginSite"))
76             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
77         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"DestinationSite"))
78             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
79         else {
80             log.error("Construction requires a valid SAML metadata file");
81             throw new MetadataException("Construction requires a valid SAML metadata file");
82         }
83         }
84
85         public EntityDescriptor lookup(String id) {
86                 ArrayList list = (ArrayList)sites.get(id);
87                 if (list != null) {
88                     long now = System.currentTimeMillis();
89             for (int i=0; i<list.size(); i++) {
90                 if (now < ((XMLEntityDescriptor)list.get(i)).getValidUntil())
91                     return (EntityDescriptor)list.get(i);
92             }
93         }
94                 return null;
95         }
96
97     public EntityDescriptor lookup(Artifact artifact) {
98         ArrayList list = null;
99         
100         if (artifact instanceof SAMLArtifactType0001) {
101             list = (ArrayList)sources.get(((SAMLArtifactType0001)artifact).getSourceId());
102         }
103         else if (artifact instanceof SAMLArtifactType0002) {
104             list = (ArrayList)sources.get(((SAMLArtifactType0002)artifact).getSourceLocation().toString());
105         }
106         else {
107             log.error("unsupported artifact type (" + artifact.getTypeCode().toString() + ")");
108         }
109
110         if (list != null) {
111             long now = System.currentTimeMillis();
112             for (int i=0; i<list.size(); i++) {
113                 if (now < ((XMLEntityDescriptor)list.get(i)).getValidUntil())
114                     return (EntityDescriptor)list.get(i);
115             }
116         }
117         return null;
118     }
119
120     class XMLEndpoint implements Endpoint {
121         private Element root = null;
122         private String binding = null;
123         private String location = null;
124         private String resploc = null;
125
126         XMLEndpoint(Element e) {
127             root = e;
128             binding = XML.assign(e.getAttributeNS(null,"Binding"));
129             location = XML.assign(e.getAttributeNS(null,"Location"));
130             resploc = XML.assign(e.getAttributeNS(null,"ResponseLocation"));
131         }
132         
133         XMLEndpoint(String binding, String location) {
134             this.binding = binding;
135             this.location = location;
136         }
137
138         public String getBinding() {
139             return binding;
140         }
141
142         public String getLocation() {
143             return location;
144         }
145
146         public String getResponseLocation() {
147             return resploc;
148         }
149         
150         public Element getElement() {
151             return root;
152         }
153     }
154
155     class XMLIndexedEndpoint extends XMLEndpoint implements IndexedEndpoint {
156         private int index = 0;
157         
158         XMLIndexedEndpoint(Element e) {
159             super(e);
160             index = Integer.parseInt(e.getAttributeNS(null,"index"));
161         }
162
163         public int getIndex() {
164             return index;
165         }
166     }
167     
168     class XMLEndpointManager implements EndpointManager {
169         private ArrayList endpoints = new ArrayList();
170         Endpoint soft = null;   // Soft default (not explicit)
171         Endpoint hard = null;   // Hard default (explicit)
172
173         public Iterator getEndpoints() {
174             return endpoints.iterator();
175         }
176         
177         public Endpoint getDefaultEndpoint() {
178             if (hard != null) return hard;
179             if (soft != null) return soft;
180             if (!endpoints.isEmpty()) return (Endpoint)endpoints.get(0);
181             return null;
182         }
183         
184         public Endpoint getEndpointByIndex(int index) {
185             for (int i=0; i < endpoints.size(); i++) {
186                 if (endpoints.get(i) instanceof IndexedEndpoint && index==((IndexedEndpoint)endpoints.get(i)).getIndex())
187                     return (Endpoint)endpoints.get(i);
188             }
189             return null;
190         }
191         
192         public Endpoint getEndpointByBinding(String binding) {
193             for (int i=0; i < endpoints.size(); i++) {
194                 if (binding.equals(((Endpoint)endpoints.get(i)).getBinding()))
195                     return (Endpoint)endpoints.get(i);
196             }
197             return null;
198         }
199         
200         protected void add(Endpoint e) {
201             endpoints.add(e);
202             if (hard == null && e.getElement() != null) {
203                 String v=XML.assign(e.getElement().getAttributeNS(null,"isDefault"));
204                 if (v != null && (v.equals("1") || v.equals("true")))  // explicit default
205                     hard=e;
206                 else if (v == null && soft == null)            // implicit default
207                     soft=e;
208             }
209             else if (hard == null && soft == null) {
210                 // No default yet, so this one qualifies as an implicit.
211                 soft=e;
212             }
213         }
214     }
215     
216     class XMLKeyDescriptor implements KeyDescriptor {
217
218         private int use = KeyDescriptor.UNSPECIFIED;
219         private KeyInfo keyInfo = null;
220         private ArrayList /* <XMLEncryptionMethod> */ methods = new ArrayList();
221
222         XMLKeyDescriptor(Element e) {
223             if (XML.safeCompare(e.getAttributeNS(null,"use"),"encryption"))
224                 use = KeyDescriptor.ENCRYPTION;
225             else if (XML.safeCompare(e.getAttributeNS(null,"use"),"signing"))
226                 use = KeyDescriptor.SIGNING;
227             
228             e = XML.getFirstChildElement(e);
229             try {
230                 keyInfo = new KeyInfo(e, null);
231             }
232             catch (XMLSecurityException e1) {
233                 log.error("unable to process ds:KeyInfo element: " + e1.getMessage());
234             }
235             
236             e = XML.getNextSiblingElement(e);
237             while (e != null && XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EncryptionMethod")) {
238                 methods.add(new XMLEncryptionMethod(e));
239             }
240         }
241
242         public int getUse() {
243             return use;
244         }
245
246         public Iterator getEncryptionMethods() {
247             return methods.iterator();
248         }
249
250         public KeyInfo getKeyInfo() {
251             return keyInfo;
252         }
253     }
254
255     class XMLEncryptionMethod implements EncryptionMethod {
256
257         String alg = null;
258         String params = null;
259         int size = 0;
260         
261         public XMLEncryptionMethod(Element e) {
262             alg = XML.assign(e.getAttributeNS(null, "Algorithm"));
263             e = XML.getFirstChildElement(e);
264             while (e != null) {
265                 if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.XMLENC_NS,"KeySize")) {
266                     if (e.hasChildNodes())
267                         size = Integer.parseInt(e.getFirstChild().getNodeValue());
268                 }
269                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.XMLENC_NS,"OAEParams")) {
270                     if (e.hasChildNodes())
271                         params = XML.assign(e.getFirstChild().getNodeValue());
272                 }
273                 e = XML.getNextSiblingElement(e);
274             }
275         }
276         
277         public String getAlgorithm() {
278             return alg;
279         }
280
281         public int getKeySize() {
282             return size;
283         }
284
285         public byte[] getOAEPparams() {
286             return params.getBytes();
287         }
288
289         public Iterator getEncryptionMethodInformation() {
290             return null;
291         }
292
293         public void setKeySize(int arg0) {
294             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
295         }
296
297         public void setOAEPparams(byte[] arg0) {
298             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
299         }
300
301         public void addEncryptionMethodInformation(Element arg0) {
302             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
303         }
304
305         public void removeEncryptionMethodInformation(Element arg0) {
306             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
307         }
308     }
309
310     class XMLKeyAuthority implements KeyAuthority {
311         private int depth = 1;
312         private ArrayList /* <KeyInfo> */ keys = new ArrayList();
313         
314         XMLKeyAuthority(Element e) {
315             if (e.hasAttributeNS(null,"VerifyDepth"))
316                 depth = Integer.parseInt(e.getAttributeNS(null,"VerifyDepth"));
317             e = XML.getFirstChildElement(e, XML.XMLSIG_NS, "KeyInfo");
318             while (e != null) {
319                 try {
320                     keys.add(new KeyInfo(e, null));
321                 }
322                 catch (XMLSecurityException e1) {
323                     log.error("unable to process ds:KeyInfo element: " + e1.getMessage());
324                 }
325                 e = XML.getNextSiblingElement(e, XML.XMLSIG_NS, "KeyInfo");
326             }
327         }
328         
329         public int getVerifyDepth() {
330             return depth;
331         }
332
333         public Iterator getKeyInfos() {
334             return keys.iterator();
335         }
336         
337     }
338         
339     class XMLOrganization implements Organization {
340         private Element root = null;
341         private HashMap /* <String,String> */ names = new HashMap();
342         private HashMap /* <String,String> */ displays = new HashMap();
343         private HashMap /* <String,URL> */ urls = new HashMap();
344
345         public XMLOrganization(Element e) throws MetadataException {
346             root = e;
347             e=XML.getFirstChildElement(e);
348             while (e != null) {
349                 if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationName")) {
350                     if (e.hasChildNodes()) {
351                         names.put(e.getAttributeNS(XML.XML_NS,"lang"),XML.assign(e.getFirstChild().getNodeValue()));
352                     }
353                 }
354                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationDisplayName")) {
355                     if (e.hasChildNodes()) {
356                         displays.put(e.getAttributeNS(XML.XML_NS,"lang"),XML.assign(e.getFirstChild().getNodeValue()));
357                     }
358                 }
359                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationURL")) {
360                     if (e.hasChildNodes()) {
361                         URL u;
362                         try {
363                             u = new URL(e.getFirstChild().getNodeValue());
364                         }
365                         catch (MalformedURLException e1) {
366                             throw new MetadataException("OrganizationURL was invalid: " + e1);
367                         }
368                         urls.put(e.getAttributeNS(XML.XML_NS,"lang"),u);
369                     }
370                 }
371                 e=XML.getNextSiblingElement(e);
372             }
373         }
374         
375         public String getName() {
376             return getName("en");
377         }
378
379         public String getName(String lang) {
380             return (String)names.get(lang);
381         }
382
383         public String getDisplayName() {
384             return getDisplayName("en");
385         }
386
387         public String getDisplayName(String lang) {
388             return (String)displays.get(lang);
389         }
390
391         public URL getURL() {
392             return getURL("en");
393         }
394
395         public URL getURL(String lang) {
396             return (URL)urls.get(lang);
397         }
398         
399     }
400     
401         class XMLContactPerson implements ContactPerson {
402             private Element root = null;
403                 private int             type;
404         private String  company = null;
405                 private String  givenName = null;
406         private String  surName = null;
407                 private ArrayList /* <String> */ emails = new ArrayList();
408         private ArrayList /* <String> */ telephones = new ArrayList();
409
410                 public XMLContactPerson(Element e) throws MetadataException {
411             root = e;
412             String rawType = null;
413             
414             // Old metadata or new?
415             if (XML.isElementNamed(root, edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Contact")) {
416                 rawType = root.getAttributeNS(null,"Type");
417                 surName = XML.assign(root.getAttributeNS(null,"Name"));
418                 if (XML.isEmpty(surName)) {
419                     throw new MetadataException("Contact is missing Name attribute.");
420                 }
421                 if (root.hasAttributeNS(null,"Email"))
422                     emails.add(e.getAttributeNS(null,"Email"));
423             }
424             else {
425                 rawType = root.getAttributeNS(null,"contactType");
426                 Node n=null;
427                 e=XML.getFirstChildElement(root);
428                 while (e != null) {
429                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Company")) {
430                         if (e.hasChildNodes())
431                             company=XML.assign(e.getFirstChild().getNodeValue());
432                     }
433                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"GivenName")) {
434                         if (e.hasChildNodes())
435                             givenName=XML.assign(e.getFirstChild().getNodeValue());
436                     }
437                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SurName")) {
438                         if (e.hasChildNodes())
439                             surName=XML.assign(e.getFirstChild().getNodeValue());
440                     }
441                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EmailAddress")) {
442                         if (e.hasChildNodes())
443                             emails.add(XML.assign(e.getFirstChild().getNodeValue()));
444                     }
445                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"TelephoneNumber")) {
446                         if (e.hasChildNodes())
447                             telephones.add(XML.assign(e.getFirstChild().getNodeValue()));
448                     }
449                     e=XML.getNextSiblingElement(e);
450                 }
451             }
452                         
453                         if (rawType.equalsIgnoreCase("TECHNICAL")) {
454                                 type = ContactPerson.TECHNICAL;
455                         } else if (rawType.equalsIgnoreCase("SUPPORT")) {
456                                 type = ContactPerson.SUPPORT;
457                         } else if (rawType.equalsIgnoreCase("ADMINISTRATIVE")) {
458                                 type = ContactPerson.ADMINISTRATIVE;
459                         } else if (rawType.equalsIgnoreCase("BILLING")) {
460                                 type = ContactPerson.BILLING;
461                         } else if (rawType.equalsIgnoreCase("OTHER")) {
462                                 type = ContactPerson.OTHER;
463                         } else {
464                                 throw new MetadataException("Contact has unknown contact type.");
465                         }
466                 }
467
468                 public int getType() {
469                         return type;
470                 }
471
472                 public String getGivenName() {
473                         return givenName;
474                 }
475
476         public String getSurName() {
477             return surName;
478         }
479         
480         public String getCompany() {
481             return company;
482         }
483
484         public Iterator getEmailAddresses() {
485                         return emails.iterator();
486                 }
487
488                 public Iterator getTelephoneNumbers() {
489                         return telephones.iterator();
490                 }
491         
492         public Element getElement() {
493             return root;
494         }
495         }
496     
497     class Role implements RoleDescriptor {
498         private Element root = null;
499         private XMLEntityDescriptor provider = null;
500         private URL errorURL = null;
501         private Organization org = null;
502         private ArrayList /* <ContactPerson> */ contacts = new ArrayList();
503         private long validUntil = Long.MAX_VALUE;
504         protected ArrayList /* <String> */ protocolEnum = new ArrayList();
505         protected ArrayList /* <KeyDescriptor> */ keys = new ArrayList();
506
507         public Role(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
508             root = e;
509             this.validUntil = validUntil;
510             this.provider = provider;
511             
512             // Check the root element namespace. If SAML2, assume it's the std schema.
513             if (e != null && edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
514                
515                 if (e.hasAttributeNS(null,"validUntil")) {
516                     SimpleDateFormat formatter = null;
517                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
518                     int dot = dateTime.indexOf('.');
519                     if (dot > 0)
520                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
521                     else
522                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
523                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
524                     try {
525                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
526                     }
527                     catch (ParseException e1) {
528                         log.warn("Role descriptor contains invalid expiration time");
529                     }
530                 }
531                 
532                 if (e.hasAttributeNS(null,"errorURL")) {
533                     try {
534                         errorURL=new URL(e.getAttributeNS(null,"errorURL"));
535                     }
536                     catch (MalformedURLException e1) {
537                         log.error("Role descriptor contains malformed errorURL");
538                     }
539                 }
540                 
541                 // Chop the protocol list into pieces...assume any whitespace can appear in between.   
542                 protocolEnum.addAll(Arrays.asList(e.getAttributeNS(null,"protocolSupportEnumeration").split("\\s")));
543                 
544                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
545                 while (e != null) {
546                     keys.add(new XMLKeyDescriptor(e));
547                     e = XML.getNextSiblingElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
548                 }
549
550                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Organization");
551                 if (e != null)
552                     org=new XMLOrganization(e);
553
554                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson");
555                 while (e != null) {
556                     contacts.add(new XMLContactPerson(e));
557                     e = XML.getNextSiblingElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson");
558                 }
559             }
560         }
561         
562         public EntityDescriptor getEntityDescriptor() {
563             return provider;
564         }
565
566         public Iterator getProtocolSupportEnumeration() {
567             return protocolEnum.iterator();
568         }
569
570         public boolean hasSupport(String version) {
571             return protocolEnum.contains(version);
572         }
573
574         public boolean isValid() {
575             return System.currentTimeMillis() < validUntil;
576         }
577
578         public URL getErrorURL() {
579             return (errorURL != null) ? errorURL : provider.getErrorURL();
580         }
581
582         public Iterator getKeyDescriptors() {
583             return keys.iterator();
584         }
585
586         public Organization getOrganization() {
587             return (org != null) ? org : provider.getOrganization();
588         }
589
590         public Iterator getContactPersons() {
591             return (contacts.isEmpty()) ? provider.getContactPersons() : contacts.iterator();
592         }
593
594         public Element getElement() {
595             return root;
596         }
597     }
598     
599     class SSORole extends Role implements SSODescriptor {
600         private XMLEndpointManager artifact = new XMLEndpointManager();
601         private XMLEndpointManager logout = new XMLEndpointManager();
602         private XMLEndpointManager nameid = new XMLEndpointManager();
603         private ArrayList /* <String> */ formats = new ArrayList();
604
605         public SSORole(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
606             super(provider, validUntil, e);
607             
608             // Check the root element namespace. If SAML2, assume it's the std schema.
609             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
610                 int i;
611                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ArtifactResolutionService");
612                 for (i=0; i<nlist.getLength(); i++)
613                     artifact.add(new XMLIndexedEndpoint((Element)nlist.item(i)));
614
615                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SingleLogoutService");
616                 for (i=0; i<nlist.getLength(); i++)
617                     logout.add(new XMLEndpoint((Element)nlist.item(i)));
618
619                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ManageNameIDService");
620                 for (i=0; i<nlist.getLength(); i++)
621                     nameid.add(new XMLEndpoint((Element)nlist.item(i)));
622
623                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"NameIDFormat");
624                 for (i = 0; i < nlist.getLength(); i++) {
625                                         if (nlist.item(i).hasChildNodes()) {
626                                                 Node tnode = nlist.item(i).getFirstChild();
627                                                 if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
628                                                         formats.add(tnode.getNodeValue());
629                                                 }
630                                         }
631                                 }
632             }
633             else {
634                 // For old style, we just do SAML 1.1 compatibility with Shib handles.
635                 protocolEnum.add(XML.SAML11_PROTOCOL_ENUM);
636                 formats.add(Constants.SHIB_NAMEID_FORMAT_URI);
637             }
638         }
639
640         public EndpointManager getArtifactResolutionServiceManager() {
641             return artifact;
642         }
643
644         public EndpointManager getSingleLogoutServiceManager() {
645             return logout;
646         }
647
648         public EndpointManager getManageNameIDServiceManager() {
649             return nameid;
650         }
651
652         public Iterator getNameIDFormats() {
653             return formats.iterator();
654         }
655     }
656     
657     class IDPRole extends SSORole implements IDPSSODescriptor, ScopedRoleDescriptor {
658         private ArrayList /* <Scope> */ scopes = new ArrayList();
659         private XMLEndpointManager sso = new XMLEndpointManager();
660         private XMLEndpointManager mapping = new XMLEndpointManager();
661         private XMLEndpointManager idreq = new XMLEndpointManager();
662         private ArrayList /* <String> */ attrprofs = new ArrayList();
663         private ArrayList /* <SAMLAttribute> */ attrs = new ArrayList();
664         private boolean wantAuthnRequestsSigned = false;
665         private String sourceId = null;
666
667         public IDPRole(XMLEntityDescriptor provider, long validUntil, Element e) throws SAMLException {
668             super(provider, validUntil, e);
669             NodeList domains=null;
670             
671             // Check the root element namespace. If SAML2, assume it's the std schema.
672             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
673                 String flag=XML.assign(e.getAttributeNS(null,"WantAuthnRequestsSigned"));
674                 wantAuthnRequestsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
675                 
676                 // Check for extensions.
677                 Element ext=XML.getFirstChildElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions");
678                 if (ext != null) {
679                     Element ext1=XML.getFirstChildElement(ext,XML.SAML_ARTIFACT_SOURCEID,"SourceID");
680                     if (ext1 != null && ext1.hasChildNodes())
681                         sourceId=ext1.getFirstChild().getNodeValue();
682                     // Save off any domain elements for later.
683                     domains = ext.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"Scope");
684                 }
685                 
686                 int i;
687                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SingleSignOnService");
688                 for (i=0; i<nlist.getLength(); i++)
689                     sso.add(new XMLEndpoint((Element)(nlist.item(i))));
690
691                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"NameIDMappingService");
692                 for (i=0; i<nlist.getLength(); i++)
693                     mapping.add(new XMLEndpoint((Element)(nlist.item(i))));
694
695                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionIDRequestService");
696                 for (i=0; i<nlist.getLength(); i++)
697                     idreq.add(new XMLEndpoint((Element)(nlist.item(i))));
698
699                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeProfile");
700                 for (i=0; i<nlist.getLength(); i++) {
701                     if (nlist.item(i).hasChildNodes())
702                         attrprofs.add(nlist.item(i).getFirstChild().getNodeValue());
703                 }
704
705                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
706                 for (i=0; i<nlist.getLength(); i++) {
707                     // For now, we need to convert these to plain SAML 1.1 attributes.
708                     Element src=(Element)(nlist.item(i));
709                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
710                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
711                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
712                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
713                     while (src != null) {
714                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
715                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
716                         NamedNodeMap attrs = src.getAttributes();
717                         for (int j=0; j<attrs.getLength(); j++)
718                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
719                         while (src.hasChildNodes())
720                             val.appendChild(src.getFirstChild());
721                         copy.appendChild(val);
722                     }
723                     attrs.add(SAMLAttribute.getInstance(copy));
724                 }
725             }
726             else {
727                 protocolEnum.add(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS);
728                 attrprofs.add(Constants.SHIB_ATTRIBUTE_NAMESPACE_URI);
729                 int i;
730                 domains = e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Domain");
731                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"HandleService");
732                 for (i=0; i<nlist.getLength(); i++) {
733                     // Manufacture an endpoint for the "Shib" binding.
734                     sso.add(
735                         new XMLEndpoint(Constants.SHIB_AUTHNREQUEST_PROFILE_URI,((Element)nlist.item(i)).getAttributeNS(null,"Location"))
736                         );
737
738                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
739                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
740                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
741                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
742                     kn.appendChild(
743                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
744                         );
745                     ki.appendChild(kn);
746                     kd.appendChild(ki);
747                     kd.setAttributeNS(null,"use","signing");
748                     keys.add(new XMLKeyDescriptor(kd));
749                 }
750             }
751             
752             if (domains != null) {
753                 for (int i=0; i < domains.getLength(); i++) {
754                     String dom=(domains.item(i).hasChildNodes()) ? domains.item(i).getFirstChild().getNodeValue() : null;
755                     if (dom != null) {
756                         String regexp=XML.assign(((Element)domains.item(i)).getAttributeNS(null,"regexp"));
757                         scopes.add(
758                             new Scope(dom,(XML.safeCompare(regexp,"true") || XML.safeCompare(regexp,"1")))
759                             );
760                     }
761                 }
762             }
763         }
764
765         public Iterator getScopes() {
766             return scopes.iterator();
767         }
768
769         public boolean getWantAuthnRequestsSigned() {
770             return wantAuthnRequestsSigned;
771         }
772
773         public EndpointManager getSingleSignOnServiceManager() {
774             return sso;
775         }
776
777         public EndpointManager getNameIDMappingServiceManager() {
778             return mapping;
779         }
780
781         public EndpointManager getAssertionIDRequestServiceManager() {
782             return idreq;
783         }
784
785         public Iterator getAttributeProfiles() {
786             return attrprofs.iterator();
787         }
788
789         public Iterator getAttributes() {
790             return attrs.iterator();
791         }
792     }
793
794     class AARole extends Role implements AttributeAuthorityDescriptor, ScopedRoleDescriptor {
795         private ArrayList /* <Scope> */ scopes = new ArrayList();
796         private XMLEndpointManager query = new XMLEndpointManager();
797         private XMLEndpointManager idreq = new XMLEndpointManager();
798         private ArrayList /* <String> */ attrprofs = new ArrayList();
799         private ArrayList /* <String> */ formats = new ArrayList();
800         private ArrayList /* <SAMLAttribute> */ attrs = new ArrayList();
801
802         public AARole(XMLEntityDescriptor provider, long validUntil, Element e) throws SAMLException {
803             super(provider, validUntil, e);
804             NodeList domains=null;
805             
806             // Check the root element namespace. If SAML2, assume it's the std schema.
807             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
808                 
809                 // Check for extensions.
810                 Element ext=XML.getFirstChildElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions");
811                 if (ext != null) {
812                     // Save off any domain elements for later.
813                     domains = ext.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"Scope");
814                 }
815                 
816                 int i;
817                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeService");
818                 for (i=0; i<nlist.getLength(); i++)
819                     query.add(new XMLEndpoint((Element)(nlist.item(i))));
820
821                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionIDRequestService");
822                 for (i=0; i<nlist.getLength(); i++)
823                     idreq.add(new XMLEndpoint((Element)(nlist.item(i))));
824
825                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeProfile");
826                 for (i=0; i<nlist.getLength(); i++) {
827                     if (nlist.item(i).hasChildNodes())
828                         attrprofs.add(nlist.item(i).getFirstChild().getNodeValue());
829                 }
830
831                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
832                 for (i=0; i<nlist.getLength(); i++) {
833                     // For now, we need to convert these to plain SAML 1.1 attributes.
834                     Element src=(Element)(nlist.item(i));
835                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
836                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
837                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
838                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
839                     while (src != null) {
840                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
841                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
842                         NamedNodeMap attrs = src.getAttributes();
843                         for (int j=0; j<attrs.getLength(); j++)
844                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
845                         while (src.hasChildNodes())
846                             val.appendChild(src.getFirstChild());
847                         copy.appendChild(val);
848                     }
849                     attrs.add(SAMLAttribute.getInstance(copy));
850                 }
851             }
852             else {
853                 // For old style, we just do SAML 1.1 compatibility with Shib handles.
854                 protocolEnum.add(XML.SAML11_PROTOCOL_ENUM);
855                 formats.add(Constants.SHIB_NAMEID_FORMAT_URI);
856                 attrprofs.add(Constants.SHIB_ATTRIBUTE_NAMESPACE_URI);
857                 domains = e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Domain");
858                 int i;
859                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeAuthority");
860                 for (i=0; i<nlist.getLength(); i++) {
861                     // Manufacture an endpoint for the SOAP binding.
862                     query.add(
863                         new XMLEndpoint(
864                             SAMLBinding.SOAP,
865                             ((Element)nlist.item(i)).getAttributeNS(null,"Location")
866                             )
867                         );
868
869                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
870                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
871                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
872                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
873                     kn.appendChild(
874                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
875                         );
876                     ki.appendChild(kn);
877                     kd.appendChild(ki);
878                     kd.setAttributeNS(null,"use","signing");
879                     keys.add(new XMLKeyDescriptor(kd));
880                 }
881             }
882
883             if (domains != null) {
884                 for (int i=0; i < domains.getLength(); i++) {
885                     String dom=(domains.item(i).hasChildNodes()) ? domains.item(i).getFirstChild().getNodeValue() : null;
886                     if (dom != null) {
887                         String regexp=XML.assign(((Element)domains.item(i)).getAttributeNS(null,"regexp"));
888                         scopes.add(
889                             new Scope(dom,(XML.safeCompare(regexp,"true") || XML.safeCompare(regexp,"1")))
890                             );
891                     }
892                 }
893             }
894         }
895
896         public Iterator getScopes() {
897             return scopes.iterator();
898         }
899
900         public EndpointManager getAttributeServiceManager() {
901             return query;
902         }
903
904         public EndpointManager getAssertionIDRequestServiceManager() {
905             return idreq;
906         }
907
908         public Iterator getAttributeProfiles() {
909             return attrprofs.iterator();
910         }
911
912         public Iterator getAttributes() {
913             return attrs.iterator();
914         }
915
916         public Iterator getNameIDFormats() {
917             return formats.iterator();
918         }
919     }
920     
921     class SPRole extends SSORole implements SPSSODescriptor {
922         private boolean authnRequestsSigned = false;
923         private boolean wantAssertionsSigned = false;
924         private XMLEndpointManager asc = new XMLEndpointManager();
925         
926         public SPRole(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
927             super(provider, validUntil, e);
928
929             // Check the root element namespace. If SAML2, assume it's the std schema.
930             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
931                 String flag=XML.assign(e.getAttributeNS(null,"AuthnRequestsSigned"));
932                 authnRequestsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
933                 flag=XML.assign(e.getAttributeNS(null,"WantAssertionsSigned"));
934                 wantAssertionsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
935                 
936                 int i;
937                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionConsumerService");
938                 for (i=0; i<nlist.getLength(); i++)
939                     asc.add(new XMLIndexedEndpoint((Element)(nlist.item(i))));
940
941                 /*
942                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
943                 for (i=0; i<nlist.getLength(); i++) {
944                     // For now, we need to convert these to plain SAML 1.1 attributes.
945                     Element src=(Element)(nlist.item(i));
946                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
947                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
948                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
949                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
950                     while (src != null) {
951                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
952                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
953                         NamedNodeMap attrs = src.getAttributes();
954                         for (int j=0; j<attrs.getLength(); j++)
955                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
956                         while (src.hasChildNodes())
957                             val.appendChild(src.getFirstChild());
958                         copy.appendChild(val);
959                     }
960                     attrs.add(SAMLAttribute.getInstance(copy));
961                 }
962                 */
963             }
964             else {
965                 int i;
966                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AssertionConsumerServiceURL");
967                 for (i=0; i<nlist.getLength(); i++) {
968                     // Manufacture an endpoint for the POST profile.
969                     asc.add(
970                         new XMLEndpoint(SAMLBrowserProfile.PROFILE_POST_URI,((Element)nlist.item(i)).getAttributeNS(null,"Location"))
971                         );
972                 }
973                 
974                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeRequester");
975                 for (i=0; i<nlist.getLength(); i++) {
976                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
977                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
978                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
979                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
980                     kn.appendChild(
981                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
982                         );
983                     ki.appendChild(kn);
984                     kd.appendChild(ki);
985                     kd.setAttributeNS(null,"use","signing");
986                     keys.add(new XMLKeyDescriptor(kd));
987                 }
988             }
989         }
990
991         public boolean getAuthnRequestsSigned() {
992             return authnRequestsSigned;
993         }
994
995         public boolean getWantAssertionsSigned() {
996             return wantAssertionsSigned;
997         }
998
999         public EndpointManager getAssertionConsumerServiceManager() {
1000             return asc;
1001         }
1002
1003         public Iterator getAttributeConsumingServices() {
1004             // TODO Auto-generated method stub
1005             return null;
1006         }
1007
1008         public AttributeConsumingService getDefaultAttributeConsumingService() {
1009             // TODO Auto-generated method stub
1010             return null;
1011         }
1012
1013         public AttributeConsumingService getAttributeConsumingServiceByID(String id) {
1014             // TODO Auto-generated method stub
1015             return null;
1016         }
1017     }
1018
1019     class AttributeRequesterRole extends Role implements AttributeRequesterDescriptor {
1020         private boolean wantAssertionsSigned = false;
1021         private ArrayList /* <String> */ formats = new ArrayList();
1022         
1023         public AttributeRequesterRole(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
1024             super(provider, validUntil, e);
1025
1026             String flag=XML.assign(e.getAttributeNS(null,"WantAssertionsSigned"));
1027             wantAssertionsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
1028
1029             NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"NameIDFormat");
1030             for (int i = 0; i < nlist.getLength(); i++) {
1031                 if (nlist.item(i).hasChildNodes()) {
1032                     Node tnode = nlist.item(i).getFirstChild();
1033                     if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
1034                         formats.add(tnode.getNodeValue());
1035                     }
1036                 }
1037             }
1038         }
1039
1040         public boolean getWantAssertionsSigned() {
1041             return wantAssertionsSigned;
1042         }
1043
1044         public Iterator getNameIDFormats() {
1045             return formats.iterator();
1046         }
1047
1048         public Iterator getAttributeConsumingServices() {
1049             // TODO Auto-generated method stub
1050             return null;
1051         }
1052
1053         public AttributeConsumingService getDefaultAttributeConsumingService() {
1054             // TODO Auto-generated method stub
1055             return null;
1056         }
1057
1058         public AttributeConsumingService getAttributeConsumingServiceByID(String id) {
1059             // TODO Auto-generated method stub
1060             return null;
1061         }
1062     }
1063     
1064     class XMLEntityDescriptor implements ExtendedEntityDescriptor {
1065         private Element root = null;
1066         private EntitiesDescriptor parent = null;
1067         private String id = null;
1068         private URL errorURL = null;
1069         private Organization org = null;
1070         private ArrayList /* <ContactPerson> */ contacts = new ArrayList();
1071         private ArrayList /* <RoleDescriptor> */ roles = new ArrayList();
1072         private AffiliationDescriptor affiliation = null;
1073         private HashMap /* <String,String> */ locs = new HashMap();
1074         private long validUntil = Long.MAX_VALUE;
1075         private ArrayList /* <KeyAuthority> */ keyauths = new ArrayList();
1076         
1077         public XMLEntityDescriptor(Element e, XMLMetadataProvider wrapper, long validUntil, EntitiesDescriptor parent) throws SAMLException {
1078             root = e;
1079             this.parent = parent;
1080             this.validUntil = validUntil;
1081
1082             // Check the root element namespace. If SAML2, assume it's the std schema.
1083             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
1084                 id=e.getAttributeNS(null,"entityID");
1085
1086                 if (e.hasAttributeNS(null,"validUntil")) {
1087                     SimpleDateFormat formatter = null;
1088                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
1089                     int dot = dateTime.indexOf('.');
1090                     if (dot > 0)
1091                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
1092                     else
1093                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
1094                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1095                     try {
1096                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
1097                     }
1098                     catch (ParseException e1) {
1099                         log.warn("Entity descriptor contains invalid expiration time");
1100                     }
1101                 }
1102
1103                 Element child=XML.getFirstChildElement(e);
1104                 while (child != null) {
1105                     // Process the various kinds of children that we care about...
1106                     if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions")) {
1107                         Element ext = XML.getFirstChildElement(child,edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"KeyAuthority");
1108                         while (ext != null) {
1109                             keyauths.add(new XMLKeyAuthority(ext));
1110                             ext = XML.getNextSiblingElement(ext,edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"KeyAuthority");
1111                         }
1112                     }
1113                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson")) {
1114                         contacts.add(new XMLContactPerson(child));
1115                     }
1116                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Organization")) {
1117                         org=new XMLOrganization(child);
1118                     }
1119                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AdditionalMetadataLocation")) {
1120                         Node loc=child.getFirstChild();
1121                         if (loc != null)
1122                             locs.put(child.getAttributeNS(null,"namespace"),loc.getNodeValue());
1123                     }
1124                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"IDPSSODescriptor")) {
1125                         roles.add(new IDPRole(this,validUntil,child));
1126                     }
1127                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeAuthorityDescriptor")) {
1128                         roles.add(new AARole(this,validUntil,child));
1129                     }
1130                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SPSSODescriptor")) {
1131                         roles.add(new SPRole(this,validUntil,child));
1132                     }
1133                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"RoleDescriptor")) {
1134                         QName xsitype = XML.getQNameAttribute(child,XML.XSI_NS,"type");
1135                         if (edu.internet2.middleware.shibboleth.common.XML.SAML2METAEXT_NS.equals(xsitype.getNamespaceURI()) &&
1136                                 "AttributeRequesterDescriptorType".equals(xsitype.getLocalPart()))
1137                             roles.add(new AttributeRequesterRole(this,validUntil,child));
1138                     }
1139                     child = XML.getNextSiblingElement(child);
1140                 }
1141             }
1142             else {
1143                 id=e.getAttributeNS(null,"Name");
1144                 if (e.hasAttributeNS(null,"ErrorURL")) {
1145                     try {
1146                         errorURL=new URL(e.getAttributeNS(null,"ErrorURL"));
1147                     }
1148                     catch (MalformedURLException e1) {
1149                         log.error("Site descriptor contains invalid ErrorURL");
1150                     }
1151                 }
1152                 
1153                 boolean idp=false,aa=false,sp=false;    // only want to build a role once
1154                 Element child=XML.getFirstChildElement(e);
1155                 while (child != null) {
1156                     // Process the various kinds of OriginSite children that we care about...
1157                     if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Contact")) {
1158                         contacts.add(new XMLContactPerson(child));
1159                     }
1160                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"HandleService") && !idp) {
1161                         // Create the IDP role if needed.
1162                         roles.add(new IDPRole(this, validUntil, e));
1163                         idp=true;
1164                     }
1165                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeAuthority") && !aa) {
1166                         // Create the AA role if needed.
1167                         roles.add(new AARole(this, validUntil, e));
1168                         aa=true;
1169                     }
1170                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AssertionConsumerServiceURL") && !sp) {
1171                         // Create the SP role if needed.
1172                         roles.add(new SPRole(this, validUntil, e));
1173                         sp=true;
1174                     }
1175                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeRequester") && !sp) {
1176                         // Create the SP role if needed.
1177                         roles.add(new SPRole(this, validUntil, e));
1178                         sp=true;
1179                     }
1180                     child = XML.getNextSiblingElement(child);
1181                 }
1182             }
1183
1184             // Each map entry is a list of the descriptors with this ID.
1185             ArrayList list;
1186             if (wrapper.sites.containsKey(id)) {
1187                 list = (ArrayList)wrapper.sites.get(id);
1188             }
1189             else {
1190                 list = new ArrayList();
1191                 wrapper.sites.put(id,list);
1192             }
1193             list.add(this);
1194             
1195             // Look for an IdP role, and register the artifact source ID and endpoints.
1196             IDPRole idp=null;
1197             for (int i=0; i<roles.size(); i++) {
1198                 if (roles.get(i) instanceof IDPRole) {
1199                     idp = (IDPRole)roles.get(i);
1200                     if (idp.sourceId != null) {
1201                         if (wrapper.sources.containsKey(idp.sourceId)) {
1202                             list = (ArrayList)wrapper.sources.get(idp.sourceId);
1203                         }
1204                         else {
1205                             list = new ArrayList();
1206                             wrapper.sources.put(idp.sourceId,list);
1207                         }
1208                         list.add(this);
1209                     }
1210                     else {
1211                         String sourceId;
1212                         try {
1213                             sourceId = new String(Hex.encode(Util.generateSourceId(id)));
1214                         }
1215                         catch (NoSuchAlgorithmException e1) {
1216                             log.error("caught exception while encoding sourceId: " + e1.getMessage());
1217                             continue;
1218                         }
1219                         if (wrapper.sources.containsKey(sourceId)) {
1220                             list = (ArrayList)wrapper.sources.get(sourceId);
1221                         }
1222                         else {
1223                             list = new ArrayList();
1224                             wrapper.sources.put(sourceId,list);
1225                         }
1226                         list.add(this);
1227                     }
1228                     Iterator locs=idp.getArtifactResolutionServiceManager().getEndpoints();
1229                     while (locs.hasNext()) {
1230                         String loc=((Endpoint)locs.next()).getLocation();
1231                         if (wrapper.sources.containsKey(loc)) {
1232                             list = (ArrayList)wrapper.sources.get(loc);
1233                         }
1234                         else {
1235                             list = new ArrayList();
1236                             wrapper.sources.put(loc,list);
1237                         }
1238                         list.add(this);
1239                     }
1240                 }
1241             }
1242         }
1243         
1244         public String getId() {
1245             return id;
1246         }
1247
1248         public boolean isValid() {
1249             return System.currentTimeMillis() < validUntil;
1250         }
1251
1252         public Iterator getRoleDescriptors() {
1253             return roles.iterator();
1254         }
1255
1256         public RoleDescriptor getRoleByType(Class type, String protocol) {
1257             for (int i=0; i<roles.size(); i++) {
1258                 RoleDescriptor role = (RoleDescriptor)roles.get(i);
1259                 if (type.isInstance(role) && role.hasSupport(protocol))
1260                     return role;
1261             }
1262             return null;
1263         }
1264
1265         public IDPSSODescriptor getIDPSSODescriptor(String protocol) {
1266             return (IDPSSODescriptor)getRoleByType(IDPSSODescriptor.class, protocol);
1267         }
1268
1269         public SPSSODescriptor getSPSSODescriptor(String protocol) {
1270             return (SPSSODescriptor)getRoleByType(SPSSODescriptor.class, protocol);
1271         }
1272
1273         public AuthnAuthorityDescriptor getAuthnAuthorityDescriptor(String protocol) {
1274             return (AuthnAuthorityDescriptor)getRoleByType(AuthnAuthorityDescriptor.class, protocol);
1275         }
1276
1277         public AttributeAuthorityDescriptor getAttributeAuthorityDescriptor(String protocol) {
1278             return (AttributeAuthorityDescriptor)getRoleByType(AttributeAuthorityDescriptor.class, protocol);
1279         }
1280
1281         public AttributeRequesterDescriptor getAttributeRequesterDescriptor(String protocol) {
1282             return (AttributeRequesterDescriptor)getRoleByType(AttributeRequesterDescriptor.class, protocol);
1283         }
1284         
1285         public PDPDescriptor getPDPDescriptor(String protocol) {
1286             return (PDPDescriptor)getRoleByType(PDPDescriptor.class, protocol);
1287         }
1288
1289         public AffiliationDescriptor getAffiliationDescriptor() {
1290             return affiliation;
1291         }
1292
1293         public Organization getOrganization() {
1294             return org;
1295         }
1296
1297         public Iterator getContactPersons() {
1298             return contacts.iterator();
1299         }
1300
1301         public Map getAdditionalMetadataLocations() {
1302             return Collections.unmodifiableMap(locs);
1303         }
1304
1305         public EntitiesDescriptor getEntitiesDescriptor() {
1306             return parent;
1307         }
1308
1309         public Element getElement() {
1310             return root;
1311         }
1312         
1313         public long getValidUntil() {
1314             return validUntil;
1315         }
1316         
1317         public URL getErrorURL() {
1318             return errorURL;
1319         }
1320
1321         public Iterator getKeyAuthorities() {
1322             return keyauths.iterator();
1323         }
1324     }
1325     
1326     class XMLEntitiesDescriptor implements ExtendedEntitiesDescriptor {
1327         private Element root = null;
1328         private EntitiesDescriptor parent = null;
1329         private String name = null;
1330         private ArrayList /* <EntitiesDescriptor> */ groups = new ArrayList();
1331         private ArrayList /* <EntityDescriptor> */ providers = new ArrayList();
1332         private long validUntil = Long.MAX_VALUE;
1333         private ArrayList /* <KeyAuthority> */ keyauths = new ArrayList();
1334         
1335         public XMLEntitiesDescriptor(Element e, XMLMetadataProvider wrapper, long validUntil, EntitiesDescriptor parent) throws SAMLException {
1336             root = e;
1337             this.parent = parent;
1338             this.validUntil = validUntil;
1339             name = XML.assign(e.getAttributeNS(null, "Name"));
1340
1341             // Check the root element namespace. If SAML2, assume it's the std schema.
1342             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
1343
1344                 if (e.hasAttributeNS(null,"validUntil")) {
1345                     SimpleDateFormat formatter = null;
1346                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
1347                     int dot = dateTime.indexOf('.');
1348                     if (dot > 0)
1349                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
1350                     else
1351                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
1352                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1353                     try {
1354                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
1355                     }
1356                     catch (ParseException e1) {
1357                         log.warn("Entities descriptor contains invalid expiration time");
1358                     }
1359                 }
1360
1361                 e = XML.getFirstChildElement(e);
1362                 while (e != null) {
1363                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions")) {
1364                         Element ext = XML.getFirstChildElement(e,edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"KeyAuthority");
1365                         while (ext != null) {
1366                             keyauths.add(new XMLKeyAuthority(ext));
1367                             ext = XML.getNextSiblingElement(ext,edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"KeyAuthority");
1368                         }
1369                     }
1370                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntitiesDescriptor"))
1371                         groups.add(new XMLEntitiesDescriptor(e, wrapper, this.validUntil, this));
1372                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntityDescriptor"))
1373                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1374                     e = XML.getNextSiblingElement(e);
1375                 }
1376             }
1377             else {
1378                 e = XML.getFirstChildElement(e);
1379                 while (e != null) {
1380                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"SiteGroup"))
1381                         groups.add(new XMLEntitiesDescriptor(e, wrapper, this.validUntil, this));
1382                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"OriginSite"))
1383                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1384                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"DestinationSite"))
1385                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1386                     e = XML.getNextSiblingElement(e);
1387                 }
1388             }
1389         }
1390
1391         public String getName() {
1392             return name;
1393         }
1394
1395         public boolean isValid() {
1396             return System.currentTimeMillis() < validUntil;
1397         }
1398
1399         public EntitiesDescriptor getEntitiesDescriptor() {
1400             return parent;
1401         }
1402
1403         public Iterator getEntitiesDescriptors() {
1404             return groups.iterator();
1405         }
1406
1407         public Iterator getEntityDescriptors() {
1408             return providers.iterator();
1409         }
1410
1411         public Element getElement() {
1412             return root;
1413         }
1414
1415         public Iterator getKeyAuthorities() {
1416             return keyauths.iterator();
1417         }
1418     }
1419 }