diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php
index c2c377e21b2bd0d83afd9600cbfa256b27808f8f..d2f8d188ed79e22f286ff20afbee2ed85e5fd522 100644
--- a/lib/SimpleSAML/Metadata/SAMLBuilder.php
+++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php
@@ -1,5 +1,6 @@
 <?php
 
+
 /**
  * Class for generating SAML 2.0 metadata from simpleSAMLphp metadata arrays.
  *
@@ -7,736 +8,761 @@
  *
  * @package simpleSAMLphp
  */
-class SimpleSAML_Metadata_SAMLBuilder {
-
-
-
-	/**
-	 * The EntityDescriptor we are building.
-	 *
-	 * @var string
-	 */
-	private $entityDescriptor;
-
-
-	/**
-	 * The maximum time in seconds the metadata should be cached.
-	 *
-	 * @var int|null
-	 */
-	private $maxCache = NULL;
-
-
-	/**
-	 * The maximum time in seconds since the current time that this metadata should be considered valid.
-	 *
-	 * @var int|null
-	 */
-	private $maxDuration = NULL;
-
-	/**
-	 * Initialize the SAML builder.
-	 *
-	 * @param string $entityId The entity id of the entity.
-	 * @param int|null $maxCache The maximum time in seconds the metadata should be cached. Defaults to null
-	 * @param int|null $maxDuration The maximum time in seconds this metadata should be considered valid. Defaults
-	 * to null.
-	 */
-	public function __construct($entityId, $maxCache = NULL, $maxDuration = NULL) {
-		assert('is_string($entityId)');
-
-		$this->maxCache = $maxCache;
-		$this->maxDuration = $maxDuration;
-
-		$this->entityDescriptor = new SAML2_XML_md_EntityDescriptor();
-		$this->entityDescriptor->entityID = $entityId;
-	}
-
-	private function setExpiration($metadata) {
-
-		if (array_key_exists('expire', $metadata)) {
-			if ($metadata['expire'] - time() < $this->maxDuration)
-				$this->maxDuration = $metadata['expire'] - time();
-		}
-
-		if ($this->maxCache !== NULL)
-			$this->entityDescriptor->cacheDuration = 'PT' . $this->maxCache . 'S';
-		if ($this->maxDuration !== NULL)
-			$this->entityDescriptor->validUntil = time() + $this->maxDuration;
-	}
-
-
-	/**
-	 * Retrieve the EntityDescriptor element which is generated for this entity.
-	 *
-	 * @return DOMElement The EntityDescriptor element of this entity.
-	 */
-	public function getEntityDescriptor() {
-
-		$xml = $this->entityDescriptor->toXML();
-		$xml->ownerDocument->appendChild($xml);
-
-		return $xml;
-	}
-
-
-	/**
-	 * Retrieve the EntityDescriptor as text.
-	 *
-	 * This function serializes this EntityDescriptor, and returns it as text.
-	 *
-	 * @param bool $formatted Whether the returned EntityDescriptor should be formatted first.
-	 * @return string The serialized EntityDescriptor.
-	 */
-	public function getEntityDescriptorText($formatted = TRUE) {
-		assert('is_bool($formatted)');
-
-		$xml = $this->getEntityDescriptor();
-		if ($formatted) {
-			SimpleSAML\Utils\XML::formatDOMElement($xml);
-		}
-
-		return $xml->ownerDocument->saveXML();
-	}
-
-
-	/**
-	 * Add a SecurityTokenServiceType for ADFS metadata.
-	 *
-	 * @param array $metadata The metadata with the information about the SecurityTokenServiceType.
-	 */
-	public function addSecurityTokenServiceType($metadata) {
-		assert('is_array($metadata)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
-
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
-                $defaultEndpoint = $metadata->getDefaultEndpoint('SingleSignOnService');
-                $e = new sspmod_adfs_SAML2_XML_fed_SecurityTokenServiceType();
-                $e->Location = $defaultEndpoint['Location'];
-
-		$this->addCertificate($e, $metadata);
-
-		$this->entityDescriptor->RoleDescriptor[] = $e;
-	}
-
-	/**
-	 * Add extensions to the metadata.
-	 *
-	 * @param SimpleSAML_Configuration $metadata The metadata to get extensions from.
-	 * @param SAML2_XML_md_RoleDescriptor $e Reference to the element where the Extensions element should be included.
-	 */
-	private function addExtensions(SimpleSAML_Configuration $metadata, SAML2_XML_md_RoleDescriptor $e) {
-
-		if ($metadata->hasValue('tags')) {
-			$a = new SAML2_XML_saml_Attribute();
-			$a->Name = 'tags';
-			foreach ($metadata->getArray('tags') as $tag) {
-				$a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($tag);
-			}
-			$e->Extensions[] = $a;
-		}
-
-		if ($metadata->hasValue('hint.cidr')) {
-			$a = new SAML2_XML_saml_Attribute();
-			$a->Name = 'hint.cidr';
-			foreach ($metadata->getArray('hint.cidr') as $hint) {
-				$a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($hint);
-			}
-			$e->Extensions[] = $a;
-		}
-
-		if ($metadata->hasValue('scope')) {
-			foreach ($metadata->getArray('scope') as $scopetext) {
-				$s = new SAML2_XML_shibmd_Scope();
-				$s->scope = $scopetext;
-				// Check whether $ ^ ( ) * | \ are in a scope -> assume regex.
-				if (1 === preg_match('/[\$\^\)\(\*\|\\\\]/', $scopetext)) {
-					$s->regexp = TRUE;
-				} else {
-					$s->regexp = FALSE;
-				}
-				$e->Extensions[] = $s;
-			}
-		}
-
-		if ($metadata->hasValue('EntityAttributes')) {
-			$ea = new SAML2_XML_mdattr_EntityAttributes();
-			foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) {
-				$a = new SAML2_XML_saml_Attribute();
-				$a->Name = $attributeName;
-				$a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
-
-				// Attribute names that is not URI is prefixed as this: '{nameformat}name'
-				if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) {
-					$a->Name = $matches[2];
-					$nameFormat = $matches[1];
-					if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
-						$a->NameFormat = $nameFormat;
-					}
-				}
-				foreach ($attributeValues as $attributeValue) {
-					$a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($attributeValue);
-				}
-				$ea->children[] = $a;
-			}
-			$this->entityDescriptor->Extensions[] = $ea;
-		}
-
-		if ($metadata->hasValue('RegistrationInfo')) {
-			$ri = new SAML2_XML_mdrpi_RegistrationInfo();
-			foreach ($metadata->getArray('RegistrationInfo') as $riName => $riValues) {
-				switch ($riName) {
-					case 'authority':
-						$ri->registrationAuthority = $riValues;
-						break;
-					case 'instant':
-						$ri->registrationInstant = SAML2_Utils::xsDateTimeToTimestamp($riValues);
-						break;
-					case 'policies':
-						$ri->RegistrationPolicy = $riValues;
-						break;
-				}
-			}
-			$this->entityDescriptor->Extensions[] = $ri;
-
-		}
-
-		if ($metadata->hasValue('UIInfo')) {
-			$ui = new SAML2_XML_mdui_UIInfo();
-			foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) {
-				switch ($uiName) {
-					case 'DisplayName':
-						$ui->DisplayName = $uiValues;
-					break;
-					case 'Description':
-						$ui->Description = $uiValues;
-					break;
-					case 'InformationURL':
-						$ui->InformationURL = $uiValues;
-					break;
-					case 'PrivacyStatementURL':
-						$ui->PrivacyStatementURL = $uiValues;
-					break;
-					case 'Keywords':
-						foreach ($uiValues as $lang => $keywords) {
-							$uiItem = new SAML2_XML_mdui_Keywords();
-							$uiItem->lang = $lang;
-							$uiItem->Keywords = $keywords;
-							$ui->Keywords[] = $uiItem;
-						}
-					break;
-					case 'Logo':
-						foreach ($uiValues as $logo) {
-							$uiItem = new SAML2_XML_mdui_Logo();
-							$uiItem->url    = $logo['url'];
-							$uiItem->width  = $logo['width'];
-							$uiItem->height = $logo['height'];
-							if (isset($logo['lang'])) {
-								$uiItem->lang = $logo['lang'];
-							}
-							$ui->Logo[] = $uiItem;
-						}
-					break;
-				}
-			}
-			$e->Extensions[] = $ui;
-		}
-
-		if ($metadata->hasValue('DiscoHints')) {
-			$dh = new SAML2_XML_mdui_DiscoHints();
-			foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) {
-				switch ($dhName) {
-					case 'IPHint':
-						$dh->IPHint = $dhValues;
-					break;
-					case 'DomainHint':
-						$dh->DomainHint = $dhValues;
-					break;
-					case 'GeolocationHint':
-						$dh->GeolocationHint = $dhValues;
-					break;
-				}
-			}
-			$e->Extensions[] = $dh;
-		}
-	}
-
-
-	/**
-	 * Add an Organization element based on data passed as parameters
-	 *
-	 * @param array $orgName An array with the localized OrganizationName.
-	 * @param array $orgDisplayName An array with the localized OrganizationDisplayName.
-	 * @param array $orgURL An array with the localized OrganizationURL.
-	 */
-	public function addOrganization(array $orgName, array $orgDisplayName, array $orgURL) {
-
-		$org = new SAML2_XML_md_Organization();
-
-		$org->OrganizationName = $orgName;
-		$org->OrganizationDisplayName = $orgDisplayName;
-		$org->OrganizationURL = $orgURL;
-
-		$this->entityDescriptor->Organization = $org;
-	}
-
-
-	/**
-	 * Add an Organization element based on metadata array.
-	 *
-	 * @param array $metadata The metadata we should extract the organization information from.
-	 */
-	public function addOrganizationInfo(array $metadata) {
-
-		if (
-			empty($metadata['OrganizationName']) ||
-			empty($metadata['OrganizationDisplayName']) ||
-			empty($metadata['OrganizationURL'])
-		    ) {
-			/* Empty or incomplete organization information. */
-			return;
-		}
-
-		$orgName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
-		$orgDisplayName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
-		$orgURL = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
-
-		$this->addOrganization($orgName, $orgDisplayName, $orgURL);
-	}
-
-
-	/**
-	 * Add a list of endpoints to metadata.
-	 *
-	 * @param array $endpoints The endpoints.
-	 * @param bool $indexed Whether the endpoints should be indexed.
-	 * @return SAML2_XML_md_IndexedEndpointType[]|SAML2_XML_md_EndpointType[] An array of endpoint objects.
-	 */
-	private static function createEndpoints(array $endpoints, $indexed) {
-		assert('is_bool($indexed)');
-
-		$ret = array();
-
-		foreach ($endpoints as &$ep) {
-			if ($indexed) {
-				$t = new SAML2_XML_md_IndexedEndpointType();
-			} else {
-				$t = new SAML2_XML_md_EndpointType();
-			}
-
-			$t->Binding = $ep['Binding'];
-			$t->Location = $ep['Location'];
-			if (isset($ep['ResponseLocation'])) {
-				$t->ResponseLocation = $ep['ResponseLocation'];
-			}
-			if (isset($ep['hoksso:ProtocolBinding'])) {
-			    $t->setAttributeNS(SAML2_Const::NS_HOK, 'hoksso:ProtocolBinding', SAML2_Const::BINDING_HTTP_REDIRECT);
-			}
-
-			if ($indexed) {
-				if (!isset($ep['index'])) {
-					/* Find the maximum index. */
-					$maxIndex = -1;
-					foreach ($endpoints as $ep) {
-						if (!isset($ep['index'])) {
-							continue;
-						}
-
-						if ($ep['index'] > $maxIndex) {
-							$maxIndex = $ep['index'];
-						}
-					}
-
-					$ep['index'] = $maxIndex + 1;
-				}
-
-				$t->index = $ep['index'];
-			}
-
-			$ret[] = $t;
-		}
-
-		return $ret;
-	}
-
-
-	/**
-	 * Add an AttributeConsumingService element to the metadata.
-	 *
-	 * @param SAML2_XML_md_SPSSODescriptor $spDesc The SPSSODescriptor element.
-	 * @param SimpleSAML_Configuration $metadata The metadata.
-	 */
-	private function addAttributeConsumingService(SAML2_XML_md_SPSSODescriptor $spDesc, SimpleSAML_Configuration $metadata) {
-		$attributes = $metadata->getArray('attributes', array());
-		$name = $metadata->getLocalizedString('name', NULL);
-
-		if ($name === NULL || count($attributes) == 0) {
-			/* We cannot add an AttributeConsumingService without name and attributes. */
-			return;
-		}
-
-		$attributesrequired = $metadata->getArray('attributes.required', array());
-
-		/*
-		 * Add an AttributeConsumingService element with information as name and description and list
-		 * of requested attributes
-		 */
-		$attributeconsumer = new SAML2_XML_md_AttributeConsumingService();
-
-		$attributeconsumer->index = 0;
-
-		$attributeconsumer->ServiceName = $name;
-		$attributeconsumer->ServiceDescription = $metadata->getLocalizedString('description', array());
-
-		$nameFormat = $metadata->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED);
-		foreach ($attributes as $friendlyName => $attribute) {
-			$t = new SAML2_XML_md_RequestedAttribute();
-			$t->Name = $attribute;
-			if (!is_int($friendlyName)) {
-				$t->FriendlyName = $friendlyName;
-			}
-			if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
-				$t->NameFormat = $nameFormat;
-			}
-			if (in_array($attribute, $attributesrequired)) {
-				$t->isRequired = true;
-			}
-			$attributeconsumer->RequestedAttribute[] = $t;
-		}
-
-		$spDesc->AttributeConsumingService[] = $attributeconsumer;
-	}
-
-
-	/**
-	 * Add a specific type of metadata to an entity.
-	 *
-	 * @param string $set The metadata set this metadata comes from.
-	 * @param array $metadata The metadata.
-	 */
-	public function addMetadata($set, $metadata) {
-		assert('is_string($set)');
-		assert('is_array($metadata)');
-		
-		$this->setExpiration($metadata);
-
-		switch ($set) {
-		case 'saml20-sp-remote':
-			$this->addMetadataSP20($metadata);
-			break;
-		case 'saml20-idp-remote':
-			$this->addMetadataIdP20($metadata);
-			break;
-		case 'shib13-sp-remote':
-			$this->addMetadataSP11($metadata);
-			break;
-		case 'shib13-idp-remote':
-			$this->addMetadataIdP11($metadata);
-			break;
-		case 'attributeauthority-remote':
-			$this->addAttributeAuthority($metadata);
-			break;
-		default:
-			SimpleSAML_Logger::warning('Unable to generate metadata for unknown type \'' . $set . '\'.');
-		}
-		
-	}
-
-	/**
-	 * Add SAML 2.0 SP metadata.
-	 *
-	 * @param array $metadata The metadata.
-	 * @param array $protocols The protocols supported. Defaults to SAML2_Const::NS_SAMLP.
-	 */
-	public function addMetadataSP20($metadata, $protocols = array(SAML2_Const::NS_SAMLP)) {
-		assert('is_array($metadata)');
-		assert('is_array($protocols)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
-
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
-
-		$e = new SAML2_XML_md_SPSSODescriptor();
-		$e->protocolSupportEnumeration = $protocols;
-
-		if ($metadata->hasValue('saml20.sign.assertion')) {
-			$e->WantAssertionsSigned = $metadata->getBoolean('saml20.sign.assertion');
-		}
-
-		if ($metadata->hasValue('redirect.validate')) {
-			$e->AuthnRequestsSigned = $metadata->getBoolean('redirect.validate');
-		} elseif ($metadata->hasValue('validate.authnrequest')) {
-			$e->AuthnRequestsSigned = $metadata->getBoolean('validate.authnrequest');
-		}
-
-		$this->addExtensions($metadata, $e);
-
-		$this->addCertificate($e, $metadata);
-
-		$e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
-
-		$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
-
-		$endpoints = $metadata->getEndpoints('AssertionConsumerService');
-		foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
-			$endpoints[] = array(
-				'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
-				'Location' => $acs,
-			);
-		}
-		$e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
-
-		$this->addAttributeConsumingService($e, $metadata);
-
-		$this->entityDescriptor->RoleDescriptor[] = $e;
-
-		foreach ($metadata->getArray('contacts', array()) as $contact) {
-			if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
-				$this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
-			}
-		}
-
-	}
-
-
-	/**
-	 * Add metadata of a SAML 2.0 identity provider.
-	 *
-	 * @param array $metadata The metadata.
-	 */
-	public function addMetadataIdP20($metadata) {
-		assert('is_array($metadata)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
-
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
-
-		$e = new SAML2_XML_md_IDPSSODescriptor();
-		$e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:2.0:protocol';
-
-		if ($metadata->hasValue('sign.authnrequest')) {
-			$e->WantAuthnRequestsSigned = $metadata->getBoolean('sign.authnrequest');
-		} elseif ($metadata->hasValue('redirect.sign')) {
-			$e->WantAuthnRequestsSigned = $metadata->getBoolean('redirect.sign');
-		}
+class SimpleSAML_Metadata_SAMLBuilder
+{
+
+
+    /**
+     * The EntityDescriptor we are building.
+     *
+     * @var string
+     */
+    private $entityDescriptor;
+
+
+    /**
+     * The maximum time in seconds the metadata should be cached.
+     *
+     * @var int|null
+     */
+    private $maxCache = null;
+
+
+    /**
+     * The maximum time in seconds since the current time that this metadata should be considered valid.
+     *
+     * @var int|null
+     */
+    private $maxDuration = null;
+
+
+    /**
+     * Initialize the SAML builder.
+     *
+     * @param string   $entityId The entity id of the entity.
+     * @param int|null $maxCache The maximum time in seconds the metadata should be cached. Defaults to null
+     * @param int|null $maxDuration The maximum time in seconds this metadata should be considered valid. Defaults
+     * to null.
+     */
+    public function __construct($entityId, $maxCache = null, $maxDuration = null)
+    {
+        assert('is_string($entityId)');
+
+        $this->maxCache = $maxCache;
+        $this->maxDuration = $maxDuration;
+
+        $this->entityDescriptor = new SAML2_XML_md_EntityDescriptor();
+        $this->entityDescriptor->entityID = $entityId;
+    }
+
+
+    private function setExpiration($metadata)
+    {
+        if (array_key_exists('expire', $metadata)) {
+            if ($metadata['expire'] - time() < $this->maxDuration) {
+                $this->maxDuration = $metadata['expire'] - time();
+            }
+        }
+
+        if ($this->maxCache !== null) {
+            $this->entityDescriptor->cacheDuration = 'PT'.$this->maxCache.'S';
+        }
+        if ($this->maxDuration !== null) {
+            $this->entityDescriptor->validUntil = time() + $this->maxDuration;
+        }
+    }
+
+
+    /**
+     * Retrieve the EntityDescriptor element which is generated for this entity.
+     *
+     * @return DOMElement The EntityDescriptor element of this entity.
+     */
+    public function getEntityDescriptor()
+    {
+        $xml = $this->entityDescriptor->toXML();
+        $xml->ownerDocument->appendChild($xml);
+
+        return $xml;
+    }
+
+
+    /**
+     * Retrieve the EntityDescriptor as text.
+     *
+     * This function serializes this EntityDescriptor, and returns it as text.
+     *
+     * @param bool $formatted Whether the returned EntityDescriptor should be formatted first.
+     *
+     * @return string The serialized EntityDescriptor.
+     */
+    public function getEntityDescriptorText($formatted = true)
+    {
+        assert('is_bool($formatted)');
 
-		$this->addExtensions($metadata, $e);
+        $xml = $this->getEntityDescriptor();
+        if ($formatted) {
+            SimpleSAML\Utils\XML::formatDOMElement($xml);
+        }
 
-		$this->addCertificate($e, $metadata);
+        return $xml->ownerDocument->saveXML();
+    }
 
-		if ($metadata->hasValue('ArtifactResolutionService')){
-			$e->ArtifactResolutionService = self::createEndpoints($metadata->getEndpoints('ArtifactResolutionService'), TRUE);
-		}
 
-		$e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
+    /**
+     * Add a SecurityTokenServiceType for ADFS metadata.
+     *
+     * @param array $metadata The metadata with the information about the SecurityTokenServiceType.
+     */
+    public function addSecurityTokenServiceType($metadata)
+    {
+        assert('is_array($metadata)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
 
-		$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+        $defaultEndpoint = $metadata->getDefaultEndpoint('SingleSignOnService');
+        $e = new sspmod_adfs_SAML2_XML_fed_SecurityTokenServiceType();
+        $e->Location = $defaultEndpoint['Location'];
 
-		$e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
+        $this->addCertificate($e, $metadata);
 
-		$this->entityDescriptor->RoleDescriptor[] = $e;
+        $this->entityDescriptor->RoleDescriptor[] = $e;
+    }
 
-		foreach ($metadata->getArray('contacts', array()) as $contact) {
-			if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
-				$this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
-			}
-		}
 
-	}
+    /**
+     * Add extensions to the metadata.
+     *
+     * @param SimpleSAML_Configuration    $metadata The metadata to get extensions from.
+     * @param SAML2_XML_md_RoleDescriptor $e Reference to the element where the Extensions element should be included.
+     */
+    private function addExtensions(SimpleSAML_Configuration $metadata, SAML2_XML_md_RoleDescriptor $e)
+    {
+        if ($metadata->hasValue('tags')) {
+            $a = new SAML2_XML_saml_Attribute();
+            $a->Name = 'tags';
+            foreach ($metadata->getArray('tags') as $tag) {
+                $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($tag);
+            }
+            $e->Extensions[] = $a;
+        }
+
+        if ($metadata->hasValue('hint.cidr')) {
+            $a = new SAML2_XML_saml_Attribute();
+            $a->Name = 'hint.cidr';
+            foreach ($metadata->getArray('hint.cidr') as $hint) {
+                $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($hint);
+            }
+            $e->Extensions[] = $a;
+        }
+
+        if ($metadata->hasValue('scope')) {
+            foreach ($metadata->getArray('scope') as $scopetext) {
+                $s = new SAML2_XML_shibmd_Scope();
+                $s->scope = $scopetext;
+                // Check whether $ ^ ( ) * | \ are in a scope -> assume regex.
+                if (1 === preg_match('/[\$\^\)\(\*\|\\\\]/', $scopetext)) {
+                    $s->regexp = true;
+                } else {
+                    $s->regexp = false;
+                }
+                $e->Extensions[] = $s;
+            }
+        }
+
+        if ($metadata->hasValue('EntityAttributes')) {
+            $ea = new SAML2_XML_mdattr_EntityAttributes();
+            foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) {
+                $a = new SAML2_XML_saml_Attribute();
+                $a->Name = $attributeName;
+                $a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
+
+                // Attribute names that is not URI is prefixed as this: '{nameformat}name'
+                if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) {
+                    $a->Name = $matches[2];
+                    $nameFormat = $matches[1];
+                    if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
+                        $a->NameFormat = $nameFormat;
+                    }
+                }
+                foreach ($attributeValues as $attributeValue) {
+                    $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($attributeValue);
+                }
+                $ea->children[] = $a;
+            }
+            $this->entityDescriptor->Extensions[] = $ea;
+        }
+
+        if ($metadata->hasValue('RegistrationInfo')) {
+            $ri = new SAML2_XML_mdrpi_RegistrationInfo();
+            foreach ($metadata->getArray('RegistrationInfo') as $riName => $riValues) {
+                switch ($riName) {
+                    case 'authority':
+                        $ri->registrationAuthority = $riValues;
+                        break;
+                    case 'instant':
+                        $ri->registrationInstant = SAML2_Utils::xsDateTimeToTimestamp($riValues);
+                        break;
+                    case 'policies':
+                        $ri->RegistrationPolicy = $riValues;
+                        break;
+                }
+            }
+            $this->entityDescriptor->Extensions[] = $ri;
+        }
+
+        if ($metadata->hasValue('UIInfo')) {
+            $ui = new SAML2_XML_mdui_UIInfo();
+            foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) {
+                switch ($uiName) {
+                    case 'DisplayName':
+                        $ui->DisplayName = $uiValues;
+                        break;
+                    case 'Description':
+                        $ui->Description = $uiValues;
+                        break;
+                    case 'InformationURL':
+                        $ui->InformationURL = $uiValues;
+                        break;
+                    case 'PrivacyStatementURL':
+                        $ui->PrivacyStatementURL = $uiValues;
+                        break;
+                    case 'Keywords':
+                        foreach ($uiValues as $lang => $keywords) {
+                            $uiItem = new SAML2_XML_mdui_Keywords();
+                            $uiItem->lang = $lang;
+                            $uiItem->Keywords = $keywords;
+                            $ui->Keywords[] = $uiItem;
+                        }
+                        break;
+                    case 'Logo':
+                        foreach ($uiValues as $logo) {
+                            $uiItem = new SAML2_XML_mdui_Logo();
+                            $uiItem->url = $logo['url'];
+                            $uiItem->width = $logo['width'];
+                            $uiItem->height = $logo['height'];
+                            if (isset($logo['lang'])) {
+                                $uiItem->lang = $logo['lang'];
+                            }
+                            $ui->Logo[] = $uiItem;
+                        }
+                        break;
+                }
+            }
+            $e->Extensions[] = $ui;
+        }
+
+        if ($metadata->hasValue('DiscoHints')) {
+            $dh = new SAML2_XML_mdui_DiscoHints();
+            foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) {
+                switch ($dhName) {
+                    case 'IPHint':
+                        $dh->IPHint = $dhValues;
+                        break;
+                    case 'DomainHint':
+                        $dh->DomainHint = $dhValues;
+                        break;
+                    case 'GeolocationHint':
+                        $dh->GeolocationHint = $dhValues;
+                        break;
+                }
+            }
+            $e->Extensions[] = $dh;
+        }
+    }
+
+
+    /**
+     * Add an Organization element based on data passed as parameters
+     *
+     * @param array $orgName An array with the localized OrganizationName.
+     * @param array $orgDisplayName An array with the localized OrganizationDisplayName.
+     * @param array $orgURL An array with the localized OrganizationURL.
+     */
+    public function addOrganization(array $orgName, array $orgDisplayName, array $orgURL)
+    {
+        $org = new SAML2_XML_md_Organization();
 
+        $org->OrganizationName = $orgName;
+        $org->OrganizationDisplayName = $orgDisplayName;
+        $org->OrganizationURL = $orgURL;
 
-	/**
-	 * Add metadata of a SAML 1.1 service provider.
-	 *
-	 * @param array $metadata The metadata.
-	 */
-	public function addMetadataSP11($metadata) {
-		assert('is_array($metadata)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
+        $this->entityDescriptor->Organization = $org;
+    }
 
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
 
-		$e = new SAML2_XML_md_SPSSODescriptor();
-		$e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+    /**
+     * Add an Organization element based on metadata array.
+     *
+     * @param array $metadata The metadata we should extract the organization information from.
+     */
+    public function addOrganizationInfo(array $metadata)
+    {
+        if (
+            empty($metadata['OrganizationName']) ||
+            empty($metadata['OrganizationDisplayName']) ||
+            empty($metadata['OrganizationURL'])
+        ) {
+            // empty or incomplete organization information
+            return;
+        }
+
+        $orgName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
+        $orgDisplayName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
+        $orgURL = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
+
+        $this->addOrganization($orgName, $orgDisplayName, $orgURL);
+    }
+
+
+    /**
+     * Add a list of endpoints to metadata.
+     *
+     * @param array $endpoints The endpoints.
+     * @param bool  $indexed Whether the endpoints should be indexed.
+     *
+     * @return SAML2_XML_md_IndexedEndpointType[]|SAML2_XML_md_EndpointType[] An array of endpoint objects.
+     */
+    private static function createEndpoints(array $endpoints, $indexed)
+    {
+        assert('is_bool($indexed)');
+
+        $ret = array();
+
+        foreach ($endpoints as &$ep) {
+            if ($indexed) {
+                $t = new SAML2_XML_md_IndexedEndpointType();
+            } else {
+                $t = new SAML2_XML_md_EndpointType();
+            }
+
+            $t->Binding = $ep['Binding'];
+            $t->Location = $ep['Location'];
+            if (isset($ep['ResponseLocation'])) {
+                $t->ResponseLocation = $ep['ResponseLocation'];
+            }
+            if (isset($ep['hoksso:ProtocolBinding'])) {
+                $t->setAttributeNS(SAML2_Const::NS_HOK, 'hoksso:ProtocolBinding', SAML2_Const::BINDING_HTTP_REDIRECT);
+            }
+
+            if ($indexed) {
+                if (!isset($ep['index'])) {
+                    /* Find the maximum index. */
+                    $maxIndex = -1;
+                    foreach ($endpoints as $ep) {
+                        if (!isset($ep['index'])) {
+                            continue;
+                        }
+
+                        if ($ep['index'] > $maxIndex) {
+                            $maxIndex = $ep['index'];
+                        }
+                    }
+
+                    $ep['index'] = $maxIndex + 1;
+                }
+
+                $t->index = $ep['index'];
+            }
+
+            $ret[] = $t;
+        }
+
+        return $ret;
+    }
+
+
+    /**
+     * Add an AttributeConsumingService element to the metadata.
+     *
+     * @param SAML2_XML_md_SPSSODescriptor $spDesc The SPSSODescriptor element.
+     * @param SimpleSAML_Configuration     $metadata The metadata.
+     */
+    private function addAttributeConsumingService(
+        SAML2_XML_md_SPSSODescriptor $spDesc,
+        SimpleSAML_Configuration $metadata
+    ) {
+        $attributes = $metadata->getArray('attributes', array());
+        $name = $metadata->getLocalizedString('name', null);
+
+        if ($name === null || count($attributes) == 0) {
+            // we cannot add an AttributeConsumingService without name and attributes
+            return;
+        }
+
+        $attributesrequired = $metadata->getArray('attributes.required', array());
+
+        /*
+         * Add an AttributeConsumingService element with information as name and description and list
+         * of requested attributes
+         */
+        $attributeconsumer = new SAML2_XML_md_AttributeConsumingService();
+
+        $attributeconsumer->index = 0;
+
+        $attributeconsumer->ServiceName = $name;
+        $attributeconsumer->ServiceDescription = $metadata->getLocalizedString('description', array());
+
+        $nameFormat = $metadata->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED);
+        foreach ($attributes as $friendlyName => $attribute) {
+            $t = new SAML2_XML_md_RequestedAttribute();
+            $t->Name = $attribute;
+            if (!is_int($friendlyName)) {
+                $t->FriendlyName = $friendlyName;
+            }
+            if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
+                $t->NameFormat = $nameFormat;
+            }
+            if (in_array($attribute, $attributesrequired)) {
+                $t->isRequired = true;
+            }
+            $attributeconsumer->RequestedAttribute[] = $t;
+        }
+
+        $spDesc->AttributeConsumingService[] = $attributeconsumer;
+    }
+
+
+    /**
+     * Add a specific type of metadata to an entity.
+     *
+     * @param string $set The metadata set this metadata comes from.
+     * @param array  $metadata The metadata.
+     */
+    public function addMetadata($set, $metadata)
+    {
+        assert('is_string($set)');
+        assert('is_array($metadata)');
+
+        $this->setExpiration($metadata);
+
+        switch ($set) {
+            case 'saml20-sp-remote':
+                $this->addMetadataSP20($metadata);
+                break;
+            case 'saml20-idp-remote':
+                $this->addMetadataIdP20($metadata);
+                break;
+            case 'shib13-sp-remote':
+                $this->addMetadataSP11($metadata);
+                break;
+            case 'shib13-idp-remote':
+                $this->addMetadataIdP11($metadata);
+                break;
+            case 'attributeauthority-remote':
+                $this->addAttributeAuthority($metadata);
+                break;
+            default:
+                SimpleSAML_Logger::warning('Unable to generate metadata for unknown type \''.$set.'\'.');
+        }
+    }
+
+
+    /**
+     * Add SAML 2.0 SP metadata.
+     *
+     * @param array $metadata The metadata.
+     * @param array $protocols The protocols supported. Defaults to SAML2_Const::NS_SAMLP.
+     */
+    public function addMetadataSP20($metadata, $protocols = array(SAML2_Const::NS_SAMLP))
+    {
+        assert('is_array($metadata)');
+        assert('is_array($protocols)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
+
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
 
-		$this->addCertificate($e, $metadata);
+        $e = new SAML2_XML_md_SPSSODescriptor();
+        $e->protocolSupportEnumeration = $protocols;
 
-		$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+        if ($metadata->hasValue('saml20.sign.assertion')) {
+            $e->WantAssertionsSigned = $metadata->getBoolean('saml20.sign.assertion');
+        }
 
-		$endpoints = $metadata->getEndpoints('AssertionConsumerService');
-		foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
-			$endpoints[] = array(
-				'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
-				'Location' => $acs,
-			);
-		}
-		$e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
+        if ($metadata->hasValue('redirect.validate')) {
+            $e->AuthnRequestsSigned = $metadata->getBoolean('redirect.validate');
+        } elseif ($metadata->hasValue('validate.authnrequest')) {
+            $e->AuthnRequestsSigned = $metadata->getBoolean('validate.authnrequest');
+        }
 
-		$this->addAttributeConsumingService($e, $metadata);
+        $this->addExtensions($metadata, $e);
 
-		$this->entityDescriptor->RoleDescriptor[] = $e;
-	}
+        $this->addCertificate($e, $metadata);
 
+        $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), false);
 
-	/**
-	 * Add metadata of a SAML 1.1 identity provider.
-	 *
-	 * @param array $metadata The metadata.
-	 */
-	public function addMetadataIdP11($metadata) {
-		assert('is_array($metadata)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
+        $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
 
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+        $endpoints = $metadata->getEndpoints('AssertionConsumerService');
+        foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
+            $endpoints[] = array(
+                'Binding'  => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
+                'Location' => $acs,
+            );
+        }
+        $e->AssertionConsumerService = self::createEndpoints($endpoints, true);
 
-		$e = new SAML2_XML_md_IDPSSODescriptor();
-		$e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
-		$e->protocolSupportEnumeration[] = 'urn:mace:shibboleth:1.0';
+        $this->addAttributeConsumingService($e, $metadata);
 
-		$this->addCertificate($e, $metadata);
+        $this->entityDescriptor->RoleDescriptor[] = $e;
+
+        foreach ($metadata->getArray('contacts', array()) as $contact) {
+            if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
+                $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
+            }
+        }
+    }
+
+
+    /**
+     * Add metadata of a SAML 2.0 identity provider.
+     *
+     * @param array $metadata The metadata.
+     */
+    public function addMetadataIdP20($metadata)
+    {
+        assert('is_array($metadata)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
 
-		$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
 
-		$e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
+        $e = new SAML2_XML_md_IDPSSODescriptor();
+        $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:2.0:protocol';
 
-		$this->entityDescriptor->RoleDescriptor[] = $e;
-	}
+        if ($metadata->hasValue('sign.authnrequest')) {
+            $e->WantAuthnRequestsSigned = $metadata->getBoolean('sign.authnrequest');
+        } elseif ($metadata->hasValue('redirect.sign')) {
+            $e->WantAuthnRequestsSigned = $metadata->getBoolean('redirect.sign');
+        }
 
+        $this->addExtensions($metadata, $e);
 
-	/**
-	 * Add metadata of a SAML attribute authority.
-	 *
-	 * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by
-	 * SimpleSAML_Metadata_SAMLParser.
-	 */
-	public function addAttributeAuthority(array $metadata) {
-		assert('is_array($metadata)');
-		assert('isset($metadata["entityid"])');
-		assert('isset($metadata["metadata-set"])');
+        $this->addCertificate($e, $metadata);
 
-		$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+        if ($metadata->hasValue('ArtifactResolutionService')) {
+            $e->ArtifactResolutionService = self::createEndpoints(
+                $metadata->getEndpoints('ArtifactResolutionService'),
+                true
+            );
+        }
 
-		$e = new SAML2_XML_md_AttributeAuthorityDescriptor();
-		$e->protocolSupportEnumeration = $metadata->getArray('protocols', array());
+        $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), false);
 
-		$this->addExtensions($metadata, $e);
-		$this->addCertificate($e, $metadata);
+        $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
 
-		$e->AttributeService = self::createEndpoints($metadata->getEndpoints('AttributeService'), FALSE);
-		$e->AssertionIDRequestService = self::createEndpoints($metadata->getEndpoints('AssertionIDRequestService'), FALSE);
+        $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), false);
 
-		$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+        $this->entityDescriptor->RoleDescriptor[] = $e;
 
-		$this->entityDescriptor->RoleDescriptor[] = $e;
-	}
+        foreach ($metadata->getArray('contacts', array()) as $contact) {
+            if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
+                $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
+            }
+        }
+    }
 
 
-	/**
-	 * Add contact information.
-	 *
-	 * Accepts a contact type, and a contact array that must be previously sanitized.
-	 *
-	 * @param string $type The type of contact. Deprecated.
-	 * @param array $details The details about the contact.
-	 *
-	 * @todo Change the signature to remove $type.
-	 * @todo Remove the capability to pass a name and parse it inside the method.
+    /**
+     * Add metadata of a SAML 1.1 service provider.
      *
-     * @deprecated This function will change its signature and no longer parse a 'name' element.
-	 */
-	public function addContact($type, $details) {
-		assert('is_string($type)');
-		assert('is_array($details)');
-		assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
-
-		// TODO: remove this check as soon as getContact() is called always before calling this function.
-		$details = \SimpleSAML\Utils\Config\Metadata::getContact($details);
-
-		$e = new SAML2_XML_md_ContactPerson();
-		$e->contactType = $type;
-
-		if (isset($details['company'])) {
-			$e->Company = $details['company'];
-		}
-		if (isset($details['givenName'])) {
-			$e->GivenName = $details['givenName'];
-		}
-		if (isset($details['surName'])) {
-			$e->SurName = $details['surName'];
-		}
-
-		if (isset($details['emailAddress'])) {
-			$eas = $details['emailAddress'];
-			if (!is_array($eas)) {
-				$eas = array($eas);
-			}
-			foreach ($eas as $ea) {
-				$e->EmailAddress[] = $ea;
-			}
-		}
-
-		if (isset($details['telephoneNumber'])) {
-			$tlfNrs = $details['telephoneNumber'];
-			if (!is_array($tlfNrs)) {
-				$tlfNrs = array($tlfNrs);
-			}
-			foreach ($tlfNrs as $tlfNr) {
-				$e->TelephoneNumber[] = $tlfNr;
-			}
-		}
-
-		$this->entityDescriptor->ContactPerson[] = $e;
-	}
-
-
-	/**
-	 * Add a KeyDescriptor with an X509 certificate.
-	 *
-	 * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
-	 * @param string $use The value of the 'use' attribute.
-	 * @param string $x509data The certificate data.
-	 */
-	private function addX509KeyDescriptor(SAML2_XML_md_RoleDescriptor $rd, $use, $x509data) {
-		assert('in_array($use, array("encryption", "signing"), TRUE)');
-		assert('is_string($x509data)');
-
-		$keyDescriptor = SAML2_Utils::createKeyDescriptor($x509data);
-		$keyDescriptor->use = $use;
-		$rd->KeyDescriptor[] = $keyDescriptor;
-	}
-
-
-	/**
-	 * Add a certificate.
-	 *
-	 * Helper function for adding a certificate to the metadata.
-	 *
-	 * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
-	 * @param SimpleSAML_Configuration $metadata The metadata of the entity.
-	 */
-	private function addCertificate(SAML2_XML_md_RoleDescriptor $rd, SimpleSAML_Configuration $metadata) {
-
-		$keys = $metadata->getPublicKeys();
-		if ($keys !== NULL) {
-			foreach ($keys as $key) {
-				if ($key['type'] !== 'X509Certificate') {
-					continue;
-				}
-				if (!isset($key['signing']) || $key['signing'] === TRUE) {
-					$this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate']);
-				}
-				if (!isset($key['encryption']) || $key['encryption'] === TRUE) {
-					$this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate']);
-				}
-			}
-		}
-
-		if ($metadata->hasValue('https.certData')) {
-			$this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData'));
-		}
-	}
+     * @param array $metadata The metadata.
+     */
+    public function addMetadataSP11($metadata)
+    {
+        assert('is_array($metadata)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
 
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+        $e = new SAML2_XML_md_SPSSODescriptor();
+        $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+
+        $this->addCertificate($e, $metadata);
+
+        $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+        $endpoints = $metadata->getEndpoints('AssertionConsumerService');
+        foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
+            $endpoints[] = array(
+                'Binding'  => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
+                'Location' => $acs,
+            );
+        }
+        $e->AssertionConsumerService = self::createEndpoints($endpoints, true);
+
+        $this->addAttributeConsumingService($e, $metadata);
+
+        $this->entityDescriptor->RoleDescriptor[] = $e;
+    }
+
+
+    /**
+     * Add metadata of a SAML 1.1 identity provider.
+     *
+     * @param array $metadata The metadata.
+     */
+    public function addMetadataIdP11($metadata)
+    {
+        assert('is_array($metadata)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
+
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+        $e = new SAML2_XML_md_IDPSSODescriptor();
+        $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+        $e->protocolSupportEnumeration[] = 'urn:mace:shibboleth:1.0';
+
+        $this->addCertificate($e, $metadata);
+
+        $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+        $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), false);
+
+        $this->entityDescriptor->RoleDescriptor[] = $e;
+    }
+
+
+    /**
+     * Add metadata of a SAML attribute authority.
+     *
+     * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by
+     * SimpleSAML_Metadata_SAMLParser.
+     */
+    public function addAttributeAuthority(array $metadata)
+    {
+        assert('is_array($metadata)');
+        assert('isset($metadata["entityid"])');
+        assert('isset($metadata["metadata-set"])');
+
+        $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+        $e = new SAML2_XML_md_AttributeAuthorityDescriptor();
+        $e->protocolSupportEnumeration = $metadata->getArray('protocols', array());
+
+        $this->addExtensions($metadata, $e);
+        $this->addCertificate($e, $metadata);
+
+        $e->AttributeService = self::createEndpoints($metadata->getEndpoints('AttributeService'), false);
+        $e->AssertionIDRequestService = self::createEndpoints(
+            $metadata->getEndpoints('AssertionIDRequestService'),
+            false
+        );
+
+        $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+        $this->entityDescriptor->RoleDescriptor[] = $e;
+    }
+
+
+    /**
+     * Add contact information.
+     *
+     * Accepts a contact type, and a contact array that must be previously sanitized.
+     *
+     * WARNING: This function will change its signature and no longer parse a 'name' element.
+     *
+     * @param string $type The type of contact. Deprecated.
+     * @param array  $details The details about the contact.
+     *
+     * @todo Change the signature to remove $type.
+     * @todo Remove the capability to pass a name and parse it inside the method.
+     */
+    public function addContact($type, $details)
+    {
+        assert('is_string($type)');
+        assert('is_array($details)');
+        assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
+
+        // TODO: remove this check as soon as getContact() is called always before calling this function.
+        $details = \SimpleSAML\Utils\Config\Metadata::getContact($details);
+
+        $e = new SAML2_XML_md_ContactPerson();
+        $e->contactType = $type;
+
+        if (isset($details['company'])) {
+            $e->Company = $details['company'];
+        }
+        if (isset($details['givenName'])) {
+            $e->GivenName = $details['givenName'];
+        }
+        if (isset($details['surName'])) {
+            $e->SurName = $details['surName'];
+        }
+
+        if (isset($details['emailAddress'])) {
+            $eas = $details['emailAddress'];
+            if (!is_array($eas)) {
+                $eas = array($eas);
+            }
+            foreach ($eas as $ea) {
+                $e->EmailAddress[] = $ea;
+            }
+        }
+
+        if (isset($details['telephoneNumber'])) {
+            $tlfNrs = $details['telephoneNumber'];
+            if (!is_array($tlfNrs)) {
+                $tlfNrs = array($tlfNrs);
+            }
+            foreach ($tlfNrs as $tlfNr) {
+                $e->TelephoneNumber[] = $tlfNr;
+            }
+        }
+
+        $this->entityDescriptor->ContactPerson[] = $e;
+    }
+
+
+    /**
+     * Add a KeyDescriptor with an X509 certificate.
+     *
+     * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
+     * @param string                      $use The value of the 'use' attribute.
+     * @param string                      $x509data The certificate data.
+     */
+    private function addX509KeyDescriptor(SAML2_XML_md_RoleDescriptor $rd, $use, $x509data)
+    {
+        assert('in_array($use, array("encryption", "signing"), TRUE)');
+        assert('is_string($x509data)');
+
+        $keyDescriptor = SAML2_Utils::createKeyDescriptor($x509data);
+        $keyDescriptor->use = $use;
+        $rd->KeyDescriptor[] = $keyDescriptor;
+    }
+
+
+    /**
+     * Add a certificate.
+     *
+     * Helper function for adding a certificate to the metadata.
+     *
+     * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
+     * @param SimpleSAML_Configuration    $metadata The metadata of the entity.
+     */
+    private function addCertificate(SAML2_XML_md_RoleDescriptor $rd, SimpleSAML_Configuration $metadata)
+    {
+        $keys = $metadata->getPublicKeys();
+        if ($keys !== null) {
+            foreach ($keys as $key) {
+                if ($key['type'] !== 'X509Certificate') {
+                    continue;
+                }
+                if (!isset($key['signing']) || $key['signing'] === true) {
+                    $this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate']);
+                }
+                if (!isset($key['encryption']) || $key['encryption'] === true) {
+                    $this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate']);
+                }
+            }
+        }
+
+        if ($metadata->hasValue('https.certData')) {
+            $this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData'));
+        }
+    }
 }