diff --git a/lib/SAML2/Assertion.php b/lib/SAML2/Assertion.php
new file mode 100644
index 0000000000000000000000000000000000000000..0fc0dbc91413354641eb94bb3b6c5ce69d296391
--- /dev/null
+++ b/lib/SAML2/Assertion.php
@@ -0,0 +1,1075 @@
+<?php
+
+/**
+ * Class representing a SAML 2 assertion.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_Assertion implements SAML2_SignedElement {
+
+	/**
+	 * The identifier of this assertion.
+	 *
+	 * @var string
+	 */
+	private $id;
+
+
+	/**
+	 * The issue timestamp of this assertion, as an UNIX timestamp.
+	 *
+	 * @var int
+	 */
+	private $issueInstant;
+
+
+	/**
+	 * The entity id of the issuer of this assertion.
+	 *
+	 * @var string
+	 */
+	private $issuer;
+
+
+	/**
+	 * The NameId of the subject in the assertion.
+	 *
+	 * If the NameId is NULL, no subject was included in the assertion.
+	 *
+	 * @var array|NULL
+	 */
+	private $nameId;
+
+
+	/**
+	 * The earliest time this assertion is valid, as an UNIX timestamp.
+	 *
+	 * @var int
+	 */
+	private $notBefore;
+
+
+	/**
+	 * The time this assertion expires, as an UNIX timestamp.
+	 *
+	 * @var int
+	 */
+	private $notOnOrAfter;
+
+
+	/**
+	 * The destination URL for this assertion.
+	 *
+	 * @var string|NULL
+	 */
+	private $destination;
+
+
+	/**
+	 * The id of the request this assertion is sent as a response to.
+	 *
+	 * This should be NULL if this isn't a response to a request.
+	 *
+	 * @var string|NULL
+	 */
+	private $inResponseTo;
+
+
+	/**
+	 * The set of audiences that are allowed to receive this assertion.
+	 *
+	 * This is an array of valid service providers.
+	 *
+	 * If no restrictions on the audience are present, this variable contains NULL.
+	 *
+	 * @var array|NULL
+	 */
+	private $validAudiences;
+
+
+	/**
+	 * The session expiration timestamp.
+	 *
+	 * @var int|NULL
+	 */
+	private $sessionNotOnOrAfter;
+
+
+	/**
+	 * The session index for this user on the IdP.
+	 *
+	 * Contains NULL if no session index is present.
+	 *
+	 * @var string|NULL
+	 */
+	private $sessionIndex;
+
+
+	/**
+	 * The authentication context for this assertion.
+	 *
+	 * @var string|NULL
+	 */
+	private $authnContext;
+
+
+	/**
+	 * The attributes, as an associative array.
+	 *
+	 * @var array
+	 */
+	private $attributes;
+
+
+	/**
+	 * The NameFormat used on all attributes.
+	 *
+	 * If more than one NameFormat is used, this will contain
+	 * the unspecified nameformat.
+	 *
+	 * @var string
+	 */
+	private $nameFormat;
+
+
+	/**
+	 * The private key we should use to sign the assertion.
+	 *
+	 * The private key can be NULL, in which case the assertion is sent unsigned.
+	 *
+	 * @var XMLSecurityKey|NULL
+	 */
+	private $signatureKey;
+
+
+	/**
+	 * List of certificates that should be included in the assertion.
+	 *
+	 * @var array
+	 */
+	private $certificates;
+
+
+	/**
+	 * The data needed to verify the signature.
+	 *
+	 * @var array|NULL
+	 */
+	private $signatureData;
+
+
+
+	/**
+	 * Constructor for SAML 2 assertions.
+	 *
+	 * @param DOMElement|NULL $xml  The input assertion.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+
+		$this->id = SimpleSAML_Utilities::generateID();
+		$this->issueInstant = time();
+		$this->issuer = '';
+		$this->attributes = array();
+		$this->nameFormat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
+		$this->certificates = array();
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		if (!$xml->hasAttribute('ID')) {
+			throw new Exception('Missing ID attribute on SAML assertion.');
+		}
+		$this->id = $xml->getAttribute('ID');
+
+		if ($xml->getAttribute('Version') !== '2.0') {
+			/* Currently a very strict check. */
+			throw new Exception('Unsupported version: ' . $xml->getAttribute('Version'));
+		}
+
+		$this->issueInstant = SimpleSAML_Utilities::parseSAML2Time($xml->getAttribute('IssueInstant'));
+
+		$issuer = SAML2_Utils::xpQuery($xml, './saml:Issuer');
+		if (empty($issuer)) {
+			throw new Exception('Missing <saml:Issuer> in assertion.');
+		}
+		$this->issuer = $issuer[0]->textContent;
+
+		$this->parseSubject($xml);
+		$this->parseConditions($xml);
+		$this->parseAuthnStatement($xml);
+		$this->parseAttributes($xml);
+		$this->parseSignature($xml);
+	}
+
+
+	/**
+	 * Parse subject in assertion.
+	 *
+	 * @param DOMElement $xml  The assertion XML element.
+	 */
+	private function parseSubject(DOMElement $xml) {
+
+		$subject = SAML2_Utils::xpQuery($xml, './saml:Subject');
+		if (empty($subject)) {
+			/* No Subject node. */
+			return;
+		} elseif (count($subject) > 1) {
+			throw new Exception('More than one <saml:Subject> in <saml:Assertion>.');
+		}
+		$subject = $subject[0];
+
+		$nameId = SAML2_Utils::xpQuery($subject, './saml:NameID');
+		if (empty($nameId)) {
+			throw new Exception('Missing <saml:NameID> in <saml:Subject>.');
+		} elseif (count($nameId) > 1) {
+			throw new Exception('More than one <saml:NameID> in <saml:Subject>.');
+		}
+		$nameId = $nameId[0];
+		$this->nameId = SAML2_Utils::parseNameId($nameId);
+
+		$subjectConfirmation = SAML2_Utils::xpQuery($subject, './saml:SubjectConfirmation');
+		if (empty($subjectConfirmation)) {
+			throw new Exception('Missing <saml:SubjectConfirmation> in <saml:Subject>.');
+		} elseif (count($subjectConfirmation) > 1) {
+			throw new Exception('More than one <saml:SubjectConfirmation> in <saml:Subject>.');
+		}
+		$subjectConfirmation = $subjectConfirmation[0];
+
+		if (!$subjectConfirmation->hasAttribute('Method')) {
+			throw new Exception('Missing required attribute "Method" on <saml:SubjectConfirmation>-node.');
+		}
+		$method = $subjectConfirmation->getAttribute('Method');
+
+		if ($method !== SAML2_Const::CM_BEARER) {
+			throw new Exception('Unsupported subject confirmation method: ' . var_export($method, TRUE));
+		}
+
+		$confirmationData = SAML2_Utils::xpQuery($subjectConfirmation, './saml:SubjectConfirmationData');
+		if (empty($confirmationData)) {
+			return;
+		} elseif (count($confirmationData) > 1) {
+			throw new Exception('More than one <saml:SubjectConfirmationData> in <saml:SubjectConfirmation> is currently unsupported.');
+		}
+		$confirmationData = $confirmationData[0];
+
+		if ($confirmationData->hasAttribute('NotBefore')) {
+			$notBefore = SimpleSAML_Utilities::parseSAML2Time($confirmationData->getAttribute('NotBefore'));
+			if ($this->notBefore === NULL || $this->notBefore < $notBefore) {
+				$this->notBefore = $notBefore;
+			}
+		}
+		if ($confirmationData->hasAttribute('NotOnOrAfter')) {
+			$notOnOrAfter = SimpleSAML_Utilities::parseSAML2Time($confirmationData->getAttribute('NotOnOrAfter'));
+			if ($this->notOnOrAfter === NULL || $this->notOnOrAfter > $notOnOrAfter) {
+				$this->notOnOrAfter = $notOnOrAfter;
+			}
+		}
+		if ($confirmationData->hasAttribute('InResponseTo')) {
+			$this->inResponseTo = $confirmationData->getAttribute('InResponseTo');;
+		}
+		if ($confirmationData->hasAttribute('Recipient')) {
+			$this->destination = $confirmationData->getAttribute('Recipient');;
+		}
+	}
+
+
+	/**
+	 * Parse conditions in assertion.
+	 *
+	 * @param DOMElement $xml  The assertion XML element.
+	 */
+	private function parseConditions(DOMElement $xml) {
+
+		$conditions = SAML2_Utils::xpQuery($xml, './saml:Conditions');
+		if (empty($conditions)) {
+			/* No <saml:Conditions> node. */
+			return;
+		} elseif (count($conditions) > 1) {
+			throw new Exception('More than one <saml:Conditions> in <saml:Assertion>.');
+		}
+		$conditions = $conditions[0];
+
+		if ($conditions->hasAttribute('NotBefore')) {
+			$notBefore = SimpleSAML_Utilities::parseSAML2Time($conditions->getAttribute('NotBefore'));
+			if ($this->notBefore === NULL || $this->notBefore < $notBefore) {
+				$this->notBefore = $notBefore;
+			}
+		}
+		if ($conditions->hasAttribute('NotOnOrAfter')) {
+			$notOnOrAfter = SimpleSAML_Utilities::parseSAML2Time($conditions->getAttribute('NotOnOrAfter'));
+			if ($this->notOnOrAfter === NULL || $this->notOnOrAfter > $notOnOrAfter) {
+				$this->notOnOrAfter = $notOnOrAfter;
+			}
+		}
+
+
+		for ($node = $conditions->firstChild; $node !== NULL; $node = $node->nextSibling) {
+			if ($node instanceof DOMText) {
+				continue;
+			}
+			if ($node->namespaceURI !== SAML2_Const::NS_SAML) {
+				throw new Exception('Unknown namespace of condition: ' . var_export($node->namespaceURI, TRUE));
+			}
+			switch ($node->localName) {
+			case 'AudienceRestriction':
+				$audiences = SAML2_Utils::xpQuery($node, './saml:Audience');
+				foreach ($audiences as &$audience) {
+					$audience = $audience->textContent;
+				}
+				if ($this->validAudiences === NULL) {
+					/* The first (and probably last) AudienceRestriction element. */
+					$this->validAudiences = $audiences;
+
+				} else {
+					/*
+					 * The set of AudienceRestriction are ANDed together, so we need
+					 * the subset that are present in all of them.
+					 */
+					$this->validAudiences = array_intersect($this->validAudiences, $audiences);
+				}
+				break;
+			case 'OneTimeUse':
+				/* Currently ignored. */
+				break;
+			case 'ProxyRestriction':
+				/* Currently ignored. */
+				break;
+			default:
+				throw new Exception('Unknown condition: ' . var_export($node->localName, TRUE));
+			}
+		}
+
+	}
+
+
+	/**
+	 * Parse AuthnStatement in assertion.
+	 *
+	 * @param DOMElement $xml  The assertion XML element.
+	 */
+	private function parseAuthnStatement(DOMElement $xml) {
+
+		$as = SAML2_Utils::xpQuery($xml, './saml:AuthnStatement');
+		if (empty($as)) {
+			return;
+		} elseif (count($as) > 1) {
+			throw new Exception('More that one <saml:AuthnStatement> in <saml:Assertion> not supported.');
+		}
+		$as = $as[0];
+		$this->authnStatement = array();
+
+		if (!$as->hasAttribute('AuthnInstant')) {
+			throw new Exception('Missing required AuthnInstant attribute on <saml:AuthnStatement>.');
+		}
+
+		if ($as->hasAttribute('SessionNotOnOrAfter')) {
+			$this->sessionNotOnOrAfter = SimpleSAML_Utilities::parseSAML2Time($as->getAttribute('SessionNotOnOrAfter'));
+		}
+
+		if ($as->hasAttribute('SessionIndex')) {
+			$this->sessionIndex = $as->getAttribute('SessionIndex');
+		}
+
+		$ac = SAML2_Utils::xpQuery($as, './saml:AuthnContext');
+		if (empty($ac)) {
+			throw new Exception('Missing required <saml:AuthnContext> in <saml:AuthnStatement>.');
+		} elseif (count($ac) > 1) {
+			throw new Exception('More than one <saml:AuthnContext> in <saml:AuthnStatement>.');
+		}
+		$ac = $ac[0];
+
+		$accr = SAML2_Utils::xpQuery($ac, './saml:AuthnContextClassRef');
+		if (empty($accr)) {
+			throw new Exception('Missing almost-required <saml:AuthnContextClassRef> in <saml:AuthnContext>.');
+		} elseif (count($accr) > 1) {
+			throw new Exception('More than one <saml:AuthnContextClassRef> in <saml:AuthnContext>.');
+		}
+		$accr = $accr[0];
+
+		$this->authnContext = $accr->textContent;
+	}
+
+
+	/**
+	 * Parse attribute statements in assertion.
+	 *
+	 * @param DOMElement $xml  The XML element with the assertion.
+	 */
+	private function parseAttributes(DOMElement $xml) {
+
+		$firstAttribute = TRUE;
+		$attributes = SAML2_Utils::xpQuery($xml, './saml:AttributeStatement/saml:Attribute');
+		foreach ($attributes as $attribute) {
+			if (!$attribute->hasAttribute('Name')) {
+				throw new Exception('Missing name on <saml:Attribute> element.');
+			}
+			$name = $attribute->getAttribute('Name');
+
+			if ($attribute->hasAttribute('NameFormat')) {
+				$nameFormat = $attribute->getAttribute('NameFormat');
+			} else {
+				$nameFormat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
+			}
+
+			if ($firstAttribute) {
+				$this->nameFormat = $nameFormat;
+				$firstAttribute = FALSE;
+			} else {
+				if ($this->nameFormat !== $nameFormat) {
+					$this->nameFormat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
+				}
+			}
+
+			if (!array_key_exists($name, $this->attributes)) {
+				$this->attributes[$name] = array();
+			}
+
+			$values = SAML2_Utils::xpQuery($attribute, './saml:AttributeValue');
+			foreach ($values as $value) {
+				$this->attributes[$name][] = $value->textContent;
+			}
+		}
+	}
+
+
+	/**
+	 * Parse signature on assertion.
+	 *
+	 * @param DOMElement $xml  The assertion XML element.
+	 */
+	private function parseSignature(DOMElement $xml) {
+
+		/* Validate the signature element of the message. */
+		$sig = SAML2_Utils::validateElement($xml);
+		if ($sig !== FALSE) {
+			$this->certificates = $sig['Certificates'];
+			$this->signatureData = $sig;
+		}
+	}
+
+
+	/**
+	 * Validate this assertion against a public key.
+	 *
+	 * If no signature was present on the assertion, we will return FALSE.
+	 * Otherwise, TRUE will be returned. An exception is thrown if the
+	 * signature validation fails.
+	 *
+	 * @param XMLSecurityKey $key  The key we should check against.
+	 * @return boolean  TRUE if successful, FALSE if it is unsigned.
+	 */
+	public function validate(XMLSecurityKey $key) {
+		assert('$key->type === XMLSecurityKey::RSA_SHA1');
+
+		if ($this->signatureData === NULL) {
+			return FALSE;
+		}
+
+		SAML2_Utils::validateSignature($this->signatureData, $key);
+
+		return TRUE;
+	}
+
+
+	/**
+	 * Retrieve the identifier of this assertion.
+	 *
+	 * @return string  The identifier of this assertion.
+	 */
+	public function getId() {
+		return $this->id;
+	}
+
+
+	/**
+	 * Set the identifier of this assertion.
+	 *
+	 * @param string $id  The new identifier of this assertion.
+	 */
+	public function setId($id) {
+		assert('is_string($id)');
+
+		$this->id = $id;
+	}
+
+
+	/**
+	 * Retrieve the issue timestamp of this assertion.
+	 *
+	 * @return int  The issue timestamp of this assertion, as an UNIX timestamp.
+	 */
+	public function getIssueInstant() {
+		return $this->issueInstant;
+	}
+
+
+	/**
+	 * Set the issue timestamp of this assertion.
+	 *
+	 * @param int $issueInstant  The new issue timestamp of this assertion, as an UNIX timestamp.
+	 */
+	public function setIssueInstant($issueInstant) {
+		assert('is_int($issueInstant)');
+
+		$this->issueInstant = $issueInstant;
+	}
+
+
+	/**
+	 * Retrieve the issuer if this assertion.
+	 *
+	 * @return string  The issuer of this assertion.
+	 */
+	public function getIssuer() {
+		return $this->issuer;
+	}
+
+
+	/**
+	 * Set the issuer of this message.
+	 *
+	 * @param string $issuer  The new issuer of this assertion.
+	 */
+	public function setIssuer($issuer) {
+		assert('is_string($issuer)');
+
+		$this->issuer = $issuer;
+	}
+
+
+	/**
+	 * Retrieve the NameId of the subject in the assertion.
+	 *
+	 * The returned NameId is in the format used by SAML2_Utils::addNameId().
+	 *
+	 * @see SAML2_Utils::addNameId()
+	 * @return array|NULL  The name identifier of the assertion.
+	 */
+	public function getNameId() {
+		return $this->nameId;
+	}
+
+
+	/**
+	 * Set the NameId of the subject in the assertion.
+	 *
+	 * The NameId must be in the format accepted by SAML2_Utils::addNameId().
+	 *
+	 * @see SAML2_Utils::addNameId()
+	 * @param array|NULL $nameId  The name identifier of the assertion.
+	 */
+	public function setNameId($nameId) {
+		assert('is_array($nameId) || is_null($nameId)');
+
+		$this->nameId = $nameId;
+	}
+
+
+	/**
+	 * Retrieve the earliest timestamp this assertion is valid.
+	 *
+	 * This function returns NULL if there are no restrictions on how early the
+	 * assertion can be used.
+	 *
+	 * @return int|NULL  The earliest timestamp this assertion is valid.
+	 */
+	public function getNotBefore() {
+
+		return $this->notBefore;
+	}
+
+
+	/**
+	 * Set the earliest timestamp this assertion can be used.
+	 *
+	 * Set this to NULL if no limit is required.
+	 *
+	 * @param int|NULL $notBefore  The earliest timestamp this assertion is valid.
+	 */
+	public function setNotBefore($notBefore) {
+		assert('is_int($notBefore) || is_null($notBefore)');
+
+		$this->notBefore = $notBefore;
+	}
+
+
+	/**
+	 * Retrieve the expiration timestamp of this assertion.
+	 *
+	 * This function returns NULL if there are no restrictions on how
+	 * late the assertion can be used.
+	 *
+	 * @return int|NULL  The latest timestamp this assertion is valid.
+	 */
+	public function getNotOnOrAfter() {
+
+		return $this->notOnOrAfter;
+	}
+
+
+	/**
+	 * Set the expiration timestamp of this assertion.
+	 *
+	 * Set this to NULL if no limit is required.
+	 *
+	 * @param int|NULL $notOnOrAfter  The latest timestamp this assertion is valid.
+	 */
+	public function setNotOnOrAfter($notOnOrAfter) {
+		assert('is_int($notOnOrAfter) || is_null($notOnOrAfter)');
+
+		$this->notOnOrAfter = $notOnOrAfter;
+	}
+
+
+	/**
+	 * Retrieve the destination URL of this assertion.
+	 *
+	 * This function returns NULL if there are no restrictions on which URL can
+	 * receive the assertion.
+	 *
+	 * @return string|NULL  The destination URL of this assertion.
+	 */
+	public function getDestination() {
+
+		return $this->destination;
+	}
+
+
+	/**
+	 * Set the destination URL of this assertion.
+	 *
+	 * @return string|NULL  The destination URL of this assertion.
+	 */
+	public function setDestination($destination) {
+		assert('is_string($destination) || is_null($destination)');
+
+		$this->destination = $destination;
+	}
+
+
+	/**
+	 * Retrieve the request this assertion is sent in response to.
+	 *
+	 * Can be NULL, in which case this assertion isn't sent in response to a specific request.
+	 *
+	 * @return string|NULL  The id of the request this assertion is sent in response to.
+	 */
+	public function getInResponseTo() {
+
+		return $this->inResponseTo;
+	}
+
+
+	/**
+	 * Set the request this assertion is sent in response to.
+	 *
+	 * Can be set to NULL, in which case this assertion isn't sent in response to a specific request.
+	 *
+	 * @param string|NULL $inResponseTo  The id of the request this assertion is sent in response to.
+	 */
+	public function setInResponseTo($inResponseTo) {
+		assert('is_string($inResponseTo) || is_null($inResponseTo)');
+
+		$this->inResponseTo = $inResponseTo;
+	}
+
+
+	/**
+	 * Retrieve the audiences that are allowed to receive this assertion.
+	 *
+	 * This may be NULL, in which case all audiences are allowed.
+	 *
+	 * @return array|NULL  The allowed audiences.
+	 */
+	public function getValidAudiences() {
+
+		return $this->validAudiences;
+	}
+
+
+	/**
+	 * Set the audiences that are allowed to receive this assertion.
+	 *
+	 * This may be NULL, in which case all audiences are allowed.
+	 *
+	 * @param array|NULL $validAudiences  The allowed audiences.
+	 */
+	public function setValidAudiences(array $validAudiences = NULL) {
+
+		$this->validAudiences = $validAudiences;
+	}
+
+
+	/**
+	 * Retrieve the session expiration timestamp.
+	 *
+	 * This function returns NULL if there are no restrictions on the
+	 * session lifetime.
+	 *
+	 * @return int|NULL  The latest timestamp this session is valid.
+	 */
+	public function getSessionNotOnOrAfter() {
+
+		return $this->sessionNotOnOrAfter;
+	}
+
+
+	/**
+	 * Set the session expiration timestamp.
+	 *
+	 * Set this to NULL if no limit is required.
+	 *
+	 * @param int|NULL $sessionLifetime  The latest timestamp this session is valid.
+	 */
+	public function setSessionNotOnOrAfter($sessionNotOnOrAfter) {
+		assert('is_int($sessionNotOnOrAfter) || is_null($sessionNotOnOrAfter)');
+
+		$this->sessionNotOnOrAfter = $sessionNotOnOrAfter;
+	}
+
+
+	/**
+	 * Retrieve the session index of the user at the IdP.
+	 *
+	 * @return string|NULL  The session index of the user at the IdP.
+	 */
+	public function getSessionIndex() {
+
+		return $this->sessionIndex;
+	}
+
+
+	/**
+	 * Set the session index of the user at the IdP.
+	 *
+	 * Note that the authentication context must be set before the
+	 * session index can be inluded in the assertion.
+	 *
+	 * @param string|NULL $sessionIndex  The session index of the user at the IdP.
+	 */
+	public function setSessionIndex($sessionIndex) {
+		assert('is_string($sessionIndex) || is_null($sessionIndex)');
+
+		$this->sessionIndex = $sessionIndex;
+	}
+
+
+	/**
+	 * Retrieve the authentication method used to authenticate the user.
+	 *
+	 * This will return NULL if no authentication statement was
+	 * included in the assertion.
+	 *
+	 * @return string|NULL  The authentication method.
+	 */
+	public function getAuthnContext() {
+
+		return $this->authnContext;
+	}
+
+
+	/**
+	 * Set the authentication method used to authenticate the user.
+	 *
+	 * If this is set to NULL, no authentication statement will be
+	 * included in the assertion. The default is NULL.
+	 *
+	 * @param string|NULL $authnContext  The authentication method.
+	 */
+	public function setAuthnContext($authnContext) {
+		assert('is_string($authnContext) || is_null($authnContext)');
+
+		$this->authnContext = $authnContext;
+	}
+
+
+	/**
+	 * Retrieve all attributes.
+	 *
+	 * @return array  All attributes, as an associative array.
+	 */
+	public function getAttributes() {
+
+		return $this->attributes;
+	}
+
+
+	/**
+	 * Replace all attributes.
+	 *
+	 * @param array $attributes  All new attributes, as an associative array.
+	 */
+	public function setAttributes(array $attributes) {
+
+		$this->attributes = $attributes;
+	}
+
+
+	/**
+	 * Retrieve the NameFormat used on all attributes.
+	 *
+	 * If more than one NameFormat is used in the received attributes, this
+	 * returns the unspecified NameFormat.
+	 *
+	 * @return string  The NameFormat used on all attributes.
+	 */
+	public function getAttributeNameFormat() {
+		return $this->nameFormat;
+	}
+
+
+	/**
+	 * Set the NameFormat used on all attributes.
+	 *
+	 * @param string $nameFormat  The NameFormat used on all attributes.
+	 */
+	public function setAttributeNameFormat($nameFormat) {
+		assert('is_string($nameFormat)');
+
+		$this->nameFormat = $nameFormat;
+	}
+
+
+	/**
+	 * Retrieve the private key we should use to sign the assertion.
+	 *
+	 * @return XMLSecurityKey|NULL The key, or NULL if no key is specified.
+	 */
+	public function getSignatureKey() {
+		return $this->signatureKey;
+	}
+
+
+	/**
+	 * Set the private key we should use to sign the assertion.
+	 *
+	 * If the key is NULL, the assertion will be sent unsigned.
+	 *
+	 * @param XMLSecurityKey|NULL $key
+	 */
+	public function setSignatureKey(XMLsecurityKey $signatureKey = NULL) {
+		$this->signatureKey = $signatureKey;
+	}
+
+
+	/**
+	 * Set the certificates that should be included in the assertion.
+	 *
+	 * The certificates should be strings with the PEM encoded data.
+	 *
+	 * @param array $certificates  An array of certificates.
+	 */
+	public function setCertificates(array $certificates) {
+		$this->certificates = $certificates;
+	}
+
+
+	/**
+	 * Retrieve the certificates that are included in the assertion.
+	 *
+	 * @return array  An array of certificates.
+	 */
+	public function getCertificates() {
+		return $this->certificates;
+	}
+
+
+	/**
+	 * Convert this assertion to an XML element.
+	 *
+	 * @return DOMElement  This assertion.
+	 */
+	public function toXML() {
+
+		$document = new DOMDocument();
+
+		$root = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:' . 'Assertion');
+		$document->appendChild($root);
+
+		/* Ugly hack to add another namespace declaration to the root element. */
+		$root->setAttributeNS(SAML2_Const::NS_SAMLP, 'samlp:tmp', 'tmp');
+		$root->removeAttributeNS(SAML2_Const::NS_SAMLP, 'tmp');
+		$root->setAttributeNS(SAML2_Const::NS_XSI, 'xsi:tmp', 'tmp');
+		$root->removeAttributeNS(SAML2_Const::NS_XSI, 'tmp');
+		$root->setAttributeNS(SAML2_Const::NS_XS, 'xs:tmp', 'tmp');
+		$root->removeAttributeNS(SAML2_Const::NS_XS, 'tmp');
+
+		$root->setAttribute('ID', $this->id);
+		$root->setAttribute('Version', '2.0');
+		$root->setAttribute('IssueInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
+
+		$issuer = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:Issuer');
+		$issuer->appendChild($document->createTextNode($this->issuer));
+		$root->appendChild($issuer);
+
+		$this->addSubject($root);
+		$this->addConditions($root);
+		$this->addAuthnStatement($root);
+		$this->addAttributeStatement($root);
+
+		if ($this->signatureKey !== NULL) {
+			SAML2_Utils::insertSignature($this->signatureKey, $this->certificates, $root, $issuer->nextSibling);
+		}
+
+		return $root;
+	}
+
+
+	/**
+	 * Add a Subject-node to the assertion.
+	 *
+	 * @param DOMElement $root  The assertion element we should add the subject to.
+	 */
+	private function addSubject(DOMElement $root) {
+
+		if ($this->nameId === NULL) {
+			/* We don't have anything to create a Subject node for. */
+			return;
+		}
+
+		$subject = $root->ownerDocument->createElementNS(SAML2_Const::NS_SAML, 'saml:Subject');
+		$root->appendChild($subject);
+
+		SAML2_Utils::addNameId($subject, $this->nameId);
+
+		$sc = $root->ownerDocument->createElementNS(SAML2_Const::NS_SAML, 'saml:SubjectConfirmation');
+		$subject->appendChild($sc);
+
+		$sc->setAttribute('Method', SAML2_Const::CM_BEARER);
+
+		$scd = $root->ownerDocument->createElementNS(SAML2_Const::NS_SAML, 'saml:SubjectConfirmationData');
+		$sc->appendChild($scd);
+
+		if ($this->notBefore !== NULL) {
+			$scd->setAttribute('NotBefore', gmdate('Y-m-d\TH:i:s\Z', $this->notBefore));
+		}
+		if ($this->notOnOrAfter !== NULL) {
+			$scd->setAttribute('NotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->notOnOrAfter));
+		}
+		if ($this->destination !== NULL) {
+			$scd->setAttribute('Recipient', $this->destination);
+		}
+		if ($this->inResponseTo !== NULL) {
+			$scd->setAttribute('InResponseTo', $this->inResponseTo);
+		}
+	}
+
+
+	/**
+	 * Add a Conditions-node to the assertion.
+	 *
+	 * @param DOMElement $root  The assertion element we should add the conditions to.
+	 */
+	private function addConditions(DOMElement $root) {
+
+		$document = $root->ownerDocument;
+
+		$conditions = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:Conditions');
+		$root->appendChild($conditions);
+
+		if ($this->notBefore !== NULL) {
+			$conditions->setAttribute('NotBefore', gmdate('Y-m-d\TH:i:s\Z', $this->notBefore));
+		}
+		if ($this->notOnOrAfter !== NULL) {
+			$conditions->setAttribute('NotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->notOnOrAfter));
+		}
+
+		if ($this->validAudiences !== NULL) {
+			$ar = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AudienceRestriction');
+			$conditions->appendChild($ar);
+
+			foreach ($this->validAudiences as $audience) {
+				$a = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:Audience');
+				$ar->appendChild($a);
+
+				$a->appendChild($document->createTextNode($audience));
+			}
+		}
+	}
+
+
+	/**
+	 * Add a AuthnStatement-node to the assertion.
+	 *
+	 * @param DOMElement $root  The assertion element we should add the authentication statement to.
+	 */
+	private function addAuthnStatement(DOMElement $root) {
+
+		if ($this->authnContext === NULL) {
+			/* No authentication context => no authentication statement. */
+			return;
+		}
+
+		$document = $root->ownerDocument;
+
+		$as = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AuthnStatement');
+		$root->appendChild($as);
+
+		$as->setAttribute('AuthnInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
+
+		if ($this->sessionNotOnOrAfter !== NULL) {
+			$as->setAttribute('SessionNotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->sessionNotOnOrAfter));
+		}
+		if ($this->sessionIndex !== NULL) {
+			$as->setAttribute('SessionIndex', $this->sessionIndex);
+		}
+
+		$ac = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AuthnContext');
+		$as->appendChild($ac);
+
+		$accr = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AuthnContextClassRef');
+		$ac->appendChild($accr);
+
+		$accr->appendChild($document->createTextNode($this->authnContext));
+	}
+
+
+	/**
+	 * Add an AttributeStatement-node to the assertion.
+	 *
+	 * @param DOMElement $root  The assertion element we should add the subject to.
+	 */
+	private function addAttributeStatement(DOMElement $root) {
+
+		if (empty($this->attributes)) {
+			return;
+		}
+
+		$document = $root->ownerDocument;
+
+		$attributeStatement = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AttributeStatement');
+		$root->appendChild($attributeStatement);
+
+		foreach ($this->attributes as $name => $values) {
+			$attribute = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:Attribute');
+			$attributeStatement->appendChild($attribute);
+			$attribute->setAttribute('Name', $name);
+
+			if ($this->nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
+				$attribute->setAttribute('NameFormat', $this->nameFormat);
+			}
+
+			foreach ($values as $value) {
+				if (is_string($value)) {
+					$type = 'xs:string';
+				} elseif (is_int($value)) {
+					$type = 'xs:integer';
+				} else {
+					$type = NULL;
+				}
+
+				$attributeValue = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:AttributeValue');
+				$attribute->appendChild($attributeValue);
+				if ($type !== NULL) {
+					$attributeValue->setAttributeNS(SAML2_Const::NS_XSI, 'xsi:type', $type);
+				}
+				$attributeValue->appendChild($document->createTextNode($value));
+			}
+		}
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/AuthnRequest.php b/lib/SAML2/AuthnRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca7bf9d9d697af7da666cd5e982bc1ec68b0135c
--- /dev/null
+++ b/lib/SAML2/AuthnRequest.php
@@ -0,0 +1,253 @@
+<?php
+
+/**
+ * Class for SAML 2 authentication request messages.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_AuthnRequest extends SAML2_Request {
+
+	/**
+	 * The options for what type of name identifier should be returned.
+	 *
+	 * @var array
+	 */
+	private $nameIdPolicy;
+
+	/**
+	 * Whether the Identity Provider must authenticate the user again.
+	 *
+	 * @var bool
+	 */
+	private $forceAuthn;
+
+
+	/**
+	 * Set to TRUE if this request is passive.
+	 *
+	 * @var bool.
+	 */
+	private $isPassive;
+
+
+	/**
+	 * The URL of the asertion consumer service where the response should be delivered.
+	 *
+	 * @var string|NULL
+	 */
+	private $assertionConsumerServiceURL;
+
+
+	/**
+	 * What binding should be used when sending the response.
+	 *
+	 * @var string|NULL
+	 */
+	private $protocolBinding;
+
+
+	/**
+	 * Constructor for SAML 2 authentication request messages.
+	 *
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+		parent::__construct('AuthnRequest', $xml);
+
+		$this->nameIdPolicy = array();
+		$this->forceAuthn = FALSE;
+		$this->isPassive = FALSE;
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		$this->forceAuthn = SAML2_Utils::parseBoolean($xml, 'ForceAuthn', FALSE);
+		$this->isPassive = SAML2_Utils::parseBoolean($xml, 'IsPassive', FALSE);
+
+		if ($xml->hasAttribute('AssertionConsumerServiceURL')) {
+			$this->assertionConsumerServiceURL = $xml->getAttribute('AssertionConsumerServiceURL');
+		}
+
+		if ($xml->hasAttribute('ProtocolBinding')) {
+			$this->protocolBinding = $xml->getAttribute('ProtocolBinding');
+		}
+
+		$nameIdPolicy = SAML2_Utils::xpQuery($xml, './samlp:NameIDPolicy');
+		if (!empty($nameIdPolicy)) {
+			$nameIdPolicy = $nameIdPolicy[0];
+			if ($nameIdPolicy->hasAttribute('Format')) {
+				$this->nameIdPolicy['Format'] = $nameIdPolicy->getAttribute('Format');
+			}
+			if ($nameIdPolicy->hasAttribute('SPNameQualifier')) {
+				$this->nameIdPolicy['SPNameQualifier'] = $nameIdPolicy->getAttribute('SPNameQualifier');
+			}
+			if ($nameIdPolicy->hasAttribute('AllowCreate')) {
+				$this->nameIdPolicy['AllowCreate'] = SAML2_Utils::parseBoolean($nameIdPolicy, 'AllowCreate', FALSE);
+			}
+		}
+	}
+
+
+	/**
+	 * Retrieve the NameIdPolicy.
+	 *
+	 * @see SAML2_AuthnRequest::setNameIdPolicy()
+	 * @return array  The NameIdPolicy.
+	 */
+	public function getNameIdPolicy() {
+		return $this->nameIdPolicy;
+	}
+
+
+	/**
+	 * Set the NameIDPolicy.
+	 *
+	 * This function accepts an array with the following options:
+	 *  - 'Format'
+	 *  - 'SPNameQualifier'
+	 *  - 'AllowCreate'
+	 *
+	 * @param array $nameIdPolicy  The NameIDPolicy.
+	 */
+	public function setNameIdPolicy(array $nameIdPolicy) {
+
+		$this->nameIdPolicy = $nameIdPolicy;
+	}
+
+
+	/**
+	 * Retrieve the value of the ForceAuthn attribute.
+	 *
+	 * @return bool  The ForceAuthn attribute.
+	 */
+	public function getForceAuthn() {
+		return $this->forceAuthn;
+	}
+
+
+	/**
+	 * Set the value of the ForceAuthn attribute.
+	 *
+	 * @param bool $forceAuthn  The ForceAuthn attribute.
+	 */
+	public function setForceAuthn($forceAuthn) {
+		assert('is_bool($forceAuthn)');
+
+		$this->forceAuthn = $forceAuthn;
+	}
+
+
+	/**
+	 * Retrieve the value of the IsPassive attribute.
+	 *
+	 * @return bool  The IsPassive attribute.
+	 */
+	public function getIsPassive() {
+		return $this->isPassive;
+	}
+
+
+	/**
+	 * Set the value of the IsPassive attribute.
+	 *
+	 * @param bool $isPassive  The IsPassive attribute.
+	 */
+	public function setIsPassive($isPassive) {
+		assert('is_bool($isPassive)');
+
+		$this->isPassive = $isPassive;
+	}
+
+
+	/**
+	 * Retrieve the value of the AssertionConsumerServiceURL attribute.
+	 *
+	 * @return string|NULL  The AssertionConsumerServiceURL attribute.
+	 */
+	public function getAssertionConsumerServiceURL() {
+		return $this->assertionConsumerServiceURL;
+	}
+
+
+	/**
+	 * Set the value of the AssertionConsumerServiceURL attribute.
+	 *
+	 * @param string|NULL $assertionConsumerServiceURL  The AssertionConsumerServiceURL attribute.
+	 */
+	public function setAssertionConsumerServiceURL($assertionConsumerServiceURL) {
+		assert('is_string($assertionConsumerServiceURL) || is_null($assertionConsumerServiceURL)');
+
+		$this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
+	}
+
+
+	/**
+	 * Retrieve the value of the ProtocolBinding attribute.
+	 *
+	 * @return string|NULL  The ProtocolBinding attribute.
+	 */
+	public function getProtocolBinding() {
+		return $this->protocolBinding;
+	}
+
+
+	/**
+	 * Set the value of the ProtocolBinding attribute.
+	 *
+	 * @param string $protocolBinding  The ProtocolBinding attribute.
+	 */
+	public function setProtocolBinding($protocolBinding) {
+		assert('is_string($protocolBinding) || is_null($protocolBinding)');
+
+		$this->protocolBinding = $protocolBinding;
+	}
+
+
+	/**
+	 * Convert this authentication request to an XML element.
+	 *
+	 * @return DOMElement  This authentication request.
+	 */
+	public function toUnsignedXML() {
+
+		$root = parent::toUnsignedXML();
+
+		if ($this->forceAuthn) {
+			$root->setAttribute('ForceAuthn', 'true');
+		}
+
+		if ($this->isPassive) {
+			$root->setAttribute('IsPassive', 'true');
+		}
+
+		if ($this->assertionConsumerServiceURL !== NULL) {
+			$root->setAttribute('AssertionConsumerServiceURL', $this->assertionConsumerServiceURL);
+		}
+
+		if ($this->protocolBinding !== NULL) {
+			$root->setAttribute('ProtocolBinding', $this->protocolBinding);
+		}
+
+		if (!empty($this->nameIdPolicy)) {
+			$nameIdPolicy = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'NameIDPolicy');
+			if (array_key_exists('Format', $this->nameIdPolicy)) {
+				$nameIdPolicy->setAttribute('Format', $this->nameIdPolicy['Format']);
+			}
+			if (array_key_exists('SPNameQualifier', $this->nameIdPolicy)) {
+				$nameIdPolicy->setAttribute('SPNameQualifier', $this->nameIdPolicy['SPNameQualifier']);
+			}
+			if (array_key_exists('AllowCreate', $this->nameIdPolicy) && $this->nameIdPolicy['AllowCreate']) {
+				$nameIdPolicy->setAttribute('AllowCreate', 'true');
+			}
+			$root->appendChild($nameIdPolicy);
+		}
+
+		return $root;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Binding.php b/lib/SAML2/Binding.php
new file mode 100644
index 0000000000000000000000000000000000000000..957ec4d732675b7045c2fa7c2bc1c60a2dfcde15
--- /dev/null
+++ b/lib/SAML2/Binding.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Base class for SAML 2 bindings.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class SAML2_Binding {
+
+	/**
+	 * The destination of messages.
+	 *
+	 * This can be NULL, in which case the destination in the message is used.
+	 */
+	protected $destination;
+
+
+	/**
+	 * Retrieve a binding with the given URN.
+	 *
+	 * Will throw an exception if it is unable to locate the binding.
+	 *
+	 * @param string $urn  The URN of the binding.
+	 * @return SAML2_Binding  The binding.
+	 */
+	public static function getBinding($urn) {
+		assert('is_string($urn)');
+
+		switch ($urn) {
+		case SAML2_Const::BINDING_HTTP_POST:
+			return new SAML2_HTTPPost();
+		case SAML2_Const::BINDING_HTTP_REDIRECT:
+			return new SAML2_HTTPRedirect();
+		default:
+			throw new Exception('Unsupported binding: ' . var_export($urn, TRUE));
+		}
+	}
+
+
+	/**
+	 * Guess the current binding.
+	 *
+	 * This function guesses the current binding and creates an instance
+	 * of SAML2_Binding matching that binding.
+	 *
+	 * An exception will be thrown if it is unable to guess the binding.
+	 *
+	 * @return SAML2_Binding  The binding.
+	 */
+	public static function getCurrentBinding() {
+		switch ($_SERVER['REQUEST_METHOD']) {
+		case 'GET':
+			return new SAML2_HTTPRedirect();
+		case 'POST':
+			return new SAML2_HTTPPost();
+		default:
+			throw new Exception('Unable to find the current binding.');
+		}
+	}
+
+
+	/**
+	 * Retrieve the destination of a message.
+	 *
+	 * @return string|NULL $destination  The destination the message will be delivered to.
+	 */
+	public function getDestination() {
+
+		return $this->destination;
+	}
+
+
+	/**
+	 * Override the destination of a message.
+	 *
+	 * Set to NULL to use the destination set in the message.
+	 *
+	 * @param string|NULL $destination  The destination the message should be delivered to.
+	 */
+	public function setDestination($destination) {
+		assert('is_string($destination) || is_null($destination)');
+
+		$this->destination = $destination;
+	}
+
+
+	/**
+	 * Send a SAML 2 message.
+	 *
+	 * This function will send a message using the specified binding.
+	 * The message will be delivered to the destination set in the message.
+	 *
+	 * @param SAML2_Message $message  The message which should be sent.
+	 */
+	abstract public function send(SAML2_Message $message);
+
+
+	/**
+	 * Receive a SAML 2 message.
+	 *
+	 * This function will extract the message from the current request.
+	 * An exception will be thrown if we are unable to process the message.
+	 *
+	 * @return SAML2_Message  The received message.
+	 */
+	abstract public function receive();
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Const.php b/lib/SAML2/Const.php
index cdb6aac124f4684eba3caa852490c3776990e90e..c88d61d8a37a5ccb7daf2b6948e50f52c0c6f0e9 100644
--- a/lib/SAML2/Const.php
+++ b/lib/SAML2/Const.php
@@ -8,6 +8,17 @@
  */
 class SAML2_Const {
 
+	/**
+	 * Password authentication context.
+	 */
+	const AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password';
+
+	/**
+	 * Unspecified authentication context.
+	 */
+	const AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified';
+
+
 	/**
 	 * The URN for the HTTP-POST binding.
 	 */
diff --git a/lib/SAML2/EncryptedAssertion.php b/lib/SAML2/EncryptedAssertion.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c22dfe3764472b296f36c202b3c042b50d9ff3c
--- /dev/null
+++ b/lib/SAML2/EncryptedAssertion.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * Class handling encrypted assertions.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_EncryptedAssertion {
+
+	/**
+	 * The current encrypted assertion.
+	 *
+	 * @var DOMElement
+	 */
+	private $encryptedData;
+
+
+	/**
+	 * Constructor for SAML 2 encrypted assertions.
+	 *
+	 * @param DOMElement|NULL $xml  The encrypted assertion XML element.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+		if ($xml === NULL) {
+			return;
+		}
+
+		$data = SAML2_Utils::xpQuery($xml, './xenc:EncryptedData');
+		if (count($data) === 0) {
+			throw new Exception('Missing encrypted data in <saml:EncryptedAssertion>.');
+		} elseif (count($data) > 1) {
+			throw new Exception('More than one encrypted data element in <saml:EncryptedAssertion>.');
+		}
+		$this->encryptedData = $data[0];
+	}
+
+
+	/**
+	 * Set the assertion.
+	 *
+	 * @param SAML2_Assertion $assertion  The assertion.
+	 * @param XMLSecurityKey $key  The key we should use to encrypt the assertion.
+	 */
+	public function setAssertion(SAML2_Assertion $assertion, XMLSecurityKey $key) {
+
+		$xml = $assertion->toXML();
+
+		$enc = new XMLSecEnc();
+		$enc->setNode($xml);
+		$enc->type = XMLSecEnc::Element;
+
+		switch ($key->type) {
+		case XMLSecurityKey::TRIPLEDES_CBC:
+		case XMLSecurityKey::AES128_CBC:
+		case XMLSecurityKey::AES192_CBC:
+		case XMLSecurityKey::AES256_CBC:
+			$symmetricKey = $key;
+			break;
+
+		case  XMLSecurityKey::RSA_1_5:
+			$symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
+			$symmetricKey->generateSessionKey();
+
+			$enc->encryptKey($key, $symmetricKey);
+
+			break;
+
+		default:
+			throw new Exception('Unknown key type for encryption: ' . $key->type);
+		}
+
+		$this->encryptedData = $enc->encryptNode($symmetricKey);
+	}
+
+
+	/**
+	 * Retrieve the assertion.
+	 *
+	 * @param XMLSecurityKey $key  The key we should use to decrypt the assertion.
+	 * @return SAML2_Assertion  The decrypted assertion.
+	 */
+	public function getAssertion(XMLSecurityKey $inputKey) {
+
+		$enc = new XMLSecEnc();
+
+		$enc->setNode($this->encryptedData);
+		$enc->type = $this->encryptedData->getAttribute("Type");
+
+		$symmetricKey = $enc->locateKey($this->encryptedData);
+		if (!$symmetricKey) {
+			throw new Exception('Could not locate key algorithm in encrypted data.');
+		}
+
+		$symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey);
+		if (!$symmetricKeyInfo) {
+			throw new Exception('Could not locate <dsig:KeyInfo> for the encrypted key.');
+		}
+
+		if ($symmetricKeyInfo->isEncrypted) {
+			/* Make sure that the input key  format is the same as the one used to encrypt the key. */
+			if ($inputKey->getAlgorith() !== $symmetricKeyInfo->getAlgorith()) {
+				throw new Exception('Algorithm mismatch between input key and key used to encrypt ' .
+					' the symmetric key for the message. Key was: ' .
+					var_export($inputKey->getAlgorith(), TRUE) . '; message was: ' .
+					var_export($symmetricKeyInfo->getAlgorith(), TRUE));
+			}
+
+			$encKey = $symmetricKeyInfo->encryptedCtx;
+			$symmetricKeyInfo->key = $inputKey->key;
+			$key = $encKey->decryptKey($symmetricKeyInfo);
+			$symmetricKey->loadkey($key);
+		} else {
+			/* Make sure that the input key has the correct format. */
+			if ($inputKey->getAlgorith() !== $symmetricKey->getAlgorith()) {
+				throw new Exception('Algorithm mismatch between input key and key in message. ' .
+					'Key was: ' . var_export($inputKey->getAlgorith(), TRUE) . '; message was: ' .
+					var_export($symmetricKey->getAlgorith(), TRUE));
+			}
+			$symmetricKey = $inputKey;
+		}
+
+		$decrypted = $enc->decryptNode($symmetricKey, FALSE);
+
+		/*
+		 * This is a workaround for the case where only a subset of the XML
+		 * tree was serialized for encryption. In that case, we may miss the
+		 * namespaces needed to parse the XML.
+		 */
+		$xml = '<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'.$decrypted.'</root>';
+		$newDoc = new DOMDocument();
+		if (!$newDoc->loadXML($xml)) {
+			throw new Exception('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?');
+		}
+		$assertionXML = $newDoc->firstChild->firstChild;
+		if ($assertionXML === NULL) {
+			throw new Exception('Missing encrypted assertion within <saml:EncryptedAssertion>.');
+		}
+		return new SAML2_Assertion($assertionXML);
+	}
+
+
+	/**
+	 * Convert this encrypted assertion to an XML element.
+	 *
+	 * @return DOMElement  This encrypted assertion.
+	 */
+	public function toXML() {
+
+		$document = new DOMDocument();
+
+		$root = $document->createElementNS(SAML2_Const::NS_SAML, 'saml:' . 'EncryptedAssertion');
+		$document->appendChild($root);
+
+		$root->appendChild($document->importNode($this->encryptedData, TRUE));
+
+		return $root;
+	}
+
+}
\ No newline at end of file
diff --git a/lib/SAML2/HTTPPost.php b/lib/SAML2/HTTPPost.php
new file mode 100644
index 0000000000000000000000000000000000000000..3a0a85c8b51a17da6f40e8bfa515241c316d9535
--- /dev/null
+++ b/lib/SAML2/HTTPPost.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Class which implements the HTTP-POST binding.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_HTTPPost extends SAML2_Binding {
+
+	/**
+	 * Send a SAML 2 message using the HTTP-POST binding.
+	 *
+	 * Note: This function never returns.
+	 *
+	 * @param SAML2_Message $message  The message we should send.
+	 */
+	public function send(SAML2_Message $message) {
+
+		if ($this->destination === NULL) {
+			$destination = $message->getDestination();
+		} else {
+			$destination = $this->destination;
+		}
+		$relayState = $message->getRelayState();
+
+		$msgStr = $message->toSignedXML();
+		$msgStr = $msgStr->ownerDocument->saveXML($msgStr);
+		$msgStr = base64_encode($msgStr);
+		$msgStr = htmlspecialchars($msgStr);
+
+		if ($message instanceof SAML2_Request) {
+			$msgType = 'SAMLRequest';
+		} else {
+			$msgType = 'SAMLResponse';
+		}
+
+		$destination = htmlspecialchars($destination);
+
+		if ($relayState !== NULL) {
+			$relayState = '<input type="hidden" name="RelayState" value="' . htmlspecialchars($relayState) . '">';
+		} else {
+			$relayState = '';
+		}
+
+		$out = <<<END
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>POST data</title>
+</head>
+<body onload="document.forms[0].submit()">
+<noscript>
+<p><strong>Note:</strong> Since your browser does not support JavaScript, you must press the button below once to proceed.</p>
+</noscript>
+<form method="post" action="$destination">
+<input type="hidden" name="$msgType" value="$msgStr" />
+$relayState
+<noscript><input type="submit" value="Submit" /></noscript>
+</form>
+</body>
+</html>
+END;
+		echo($out);
+		exit(0);
+	}
+
+
+	/**
+	 * Receive a SAML 2 message sent using the HTTP-POST binding.
+	 *
+	 * Throws an exception if it is unable receive the message.
+	 *
+	 * @return SAML2_Message  The received message.
+	 */
+	public function receive() {
+
+		if (array_key_exists('SAMLRequest', $_POST)) {
+			$msg = $_POST['SAMLRequest'];
+		} elseif (array_key_exists('SAMLResponse', $_POST)) {
+			$msg = $_POST['SAMLResponse'];
+		} else {
+			throw new Exception('Missing SAMLRequest or SAMLResponse parameter.');
+		}
+
+		$msg = base64_decode($msg);
+
+		$document = new DOMDocument();
+		$document->loadXML($msg);
+		$xml = $document->firstChild;
+
+		$msg = SAML2_Message::fromXML($xml);
+
+		if (array_key_exists('RelayState', $_POST)) {
+			$msg->setRelayState($_POST['RelayState']);
+		}
+
+		return $msg;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/HTTPRedirect.php b/lib/SAML2/HTTPRedirect.php
new file mode 100644
index 0000000000000000000000000000000000000000..71a32b42a1dc58382c694ceff955fe2fbc40cf77
--- /dev/null
+++ b/lib/SAML2/HTTPRedirect.php
@@ -0,0 +1,229 @@
+<?php
+
+/**
+ * Class which implements the HTTP-Redirect binding.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_HTTPRedirect extends SAML2_Binding {
+
+	const DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE';
+
+	/**
+	 * Create the redirect URL for a message.
+	 *
+	 * @param SAML2_Message $message  The message.
+	 * @return string  The URL the user should be redirected to in order to send a message.
+	 */
+	public function getRedirectURL(SAML2_Message $message) {
+
+		if ($this->destination === NULL) {
+			$destination = $message->getDestination();
+		} else {
+			$destination = $this->destination;
+		}
+
+		$relayState = $message->getRelayState();
+
+		$key = $message->getSignatureKey();
+
+		$msgStr = $message->toUnsignedXML();
+		$msgStr = $msgStr->ownerDocument->saveXML($msgStr);
+		$msgStr = gzdeflate($msgStr);
+		$msgStr = base64_encode($msgStr);
+
+		/* Build the query string. */
+
+		if ($message instanceof SAML2_Request) {
+			$msg = 'SAMLRequest=';
+		} else {
+			$msg = 'SAMLResponse=';
+		}
+		$msg .= urlencode($msgStr);
+
+		if ($relayState !== NULL) {
+			$msg .= '&RelayState=' . urlencode($relayState);
+		}
+
+		if ($key !== NULL) {
+			/* Add the signature. */
+			$msg .= '&SigAlg=' . urlencode(XMLSecurityKey::RSA_SHA1);
+
+			$signature = $key->signData($msg);
+			$msg .= '&Signature=' . urlencode(base64_encode($signature));
+		}
+
+		if (strpos($destination, '?') === FALSE) {
+			$destination .= '?' . $msg;
+		} else {
+			$destination .= '&' . $msg;
+		}
+
+		return $destination;
+	}
+
+
+	/**
+	 * Send a SAML 2 message using the HTTP-Redirect binding.
+	 *
+	 * Note: This function never returns.
+	 *
+	 * @param SAML2_Message $message  The message we should send.
+	 */
+	public function send(SAML2_Message $message) {
+
+		$destination = $this->getRedirectURL($message);
+		SimpleSAML_Utilities::redirect($destination);
+	}
+
+
+	/**
+	 * Receive a SAML 2 message sent using the HTTP-Redirect binding.
+	 *
+	 * Throws an exception if it is unable receive the message.
+	 *
+	 * @return SAML2_Message  The received message.
+	 */
+	public function receive() {
+
+		$data = self::parseQuery();
+
+		if (array_key_exists('SAMLRequest', $data)) {
+			$msg = $data['SAMLRequest'];
+		} elseif (array_key_exists('SAMLResponse', $data)) {
+			$msg = $data['SAMLResponse'];
+		} else {
+			throw new Execption('Missing SAMLRequest or SAMLResponse parameter.');
+		}
+
+		if (array_key_exists('SAMLEncoding', $data)) {
+			$encoding = $data['SAMLEncoding'];
+		} else {
+			$encoding = self::DEFLATE;
+		}
+
+		$msg = base64_decode($msg);
+		switch ($encoding) {
+		case self::DEFLATE:
+			$msg = gzinflate($msg);
+			break;
+		default:
+			throw new Exception('Unknown SAMLEncoding: ' . var_export($encoding, TRUE));
+		}
+
+		$document = new DOMDocument();
+		$document->loadXML($msg);
+		$xml = $document->firstChild;
+
+		$msg = SAML2_Message::fromXML($xml);
+
+		if (array_key_exists('Signature', $data)) {
+			/* Save the signature validation data until we need it. */
+			$signatureValidationData = array(
+				'Signature' => $data['Signature'],
+				'Query' => $data['SignedQuery'],
+				);
+		}
+
+
+		if (array_key_exists('RelayState', $data)) {
+			$msg->setRelayState($data['RelayState']);
+		}
+
+		if (array_key_exists('Signature', $data)) {
+			if (!array_key_exists('SigAlg', $data)) {
+				throw new Exception('Missing signature algorithm.');
+			}
+
+			$signData = array(
+				'Signature' => $data['Signature'],
+				'SigAlg' => $data['SigAlg'],
+				'Query' => $data['SignedQuery'],
+				);
+			$msg->addValidator(array(get_class($this), 'validateSignature'), $signData);
+		}
+
+		return $msg;
+	}
+
+
+	/**
+	 * Helper function to parse query data.
+	 *
+	 * This function returns the query string split into key=>value pairs.
+	 * It also adds a new parameter, SignedQuery, which contains the data that is
+	 * signed.
+	 *
+	 * @return string  The query data that is signed.
+	 */
+	private static function parseQuery() {
+		/*
+		 * Parse the query string. We need to do this ourself, so that we get access
+		 * to the raw (urlencoded) values. This is required because different software
+		 * can urlencode to different values.
+		 */
+		$data = array();
+		$relayState = '';
+		$sigAlg = '';
+		foreach (explode('&', $_SERVER['QUERY_STRING']) as $e) {
+			list($name, $value) = explode('=', $e, 2);
+			$name = urldecode($name);
+			$data[$name] = urldecode($value);
+
+			switch ($name) {
+			case 'SAMLRequest':
+			case 'SAMLResponse':
+				$sigQuery = $name . '=' . $value;
+				break;
+			case 'RelayState':
+				$relayState = '&RelayState=' . $value;
+				break;
+			case 'SigAlg':
+				$sigAlg = '&SigAlg=' . $value;
+				break;
+			}
+		}
+
+		$data['SignedQuery'] = $sigQuery . $relayState . $sigAlg;
+
+		return $data;
+	}
+
+
+	/**
+	 * Validate the signature on a HTTP-Redirect message.
+	 *
+	 * Throws an exception if we are unable to validate the signature.
+	 *
+	 * @param array $data  The data we need to validate the query string.
+	 * @param XMLSecurityKey $key  The key we should validate the query against.
+	 */
+	public static function validateSignature(array $data, XMLSecurityKey $key) {
+		assert('array_key_exists("Query", $data)');
+		assert('array_key_exists("SigAlg", $data)');
+		assert('array_key_exists("Signature", $data)');
+
+		$query = $data['Query'];
+		$sigAlg = $data['SigAlg'];
+		$signature = $data['Signature'];
+
+		$signature = base64_decode($signature);
+
+		switch ($sigAlg) {
+		case XMLSecurityKey::RSA_SHA1:
+			if ($key->type !== XMLSecurityKey::RSA_SHA1) {
+				throw new Exception('Invalid key type for validating signature on query string.');
+			}
+			if (!$key->verifySignature($query,$signature)) {
+				throw new Exception('Unable to validate signature on query string.');
+			}
+			break;
+		default:
+			throw new Exception('Unknown signature algorithm: ' . var_export($sigAlg, TRUE));
+		}
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/LogoutRequest.php b/lib/SAML2/LogoutRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f239c9a01d4ab73209f95a0cadb62ac242e6a54a
--- /dev/null
+++ b/lib/SAML2/LogoutRequest.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Class for SAML 2 logout request messages.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_LogoutRequest extends SAML2_Request {
+
+
+	/**
+	 * The name identifier of the session that should be terminated.
+	 *
+	 * @var array
+	 */
+	private $nameId;
+
+
+	/**
+	 * The session index of the session that should be terminated.
+	 *
+	 * @var string|NULL
+	 */
+	private $sessionIndex;
+
+
+	/**
+	 * Constructor for SAML 2 logout request messages.
+	 *
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+		parent::__construct('LogoutRequest', $xml);
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		$nameId = SAML2_Utils::xpQuery($xml, './saml:NameID');
+		if (empty($nameId)) {
+			throw new Exception('Missing NameID in logout request.');
+		}
+		$this->nameId = SAML2_Utils::parseNameId($nameId[0]);
+
+		$sessionIndex = SAML2_Utils::xpQuery($xml, './samlp:SessionIndex');
+		if (!empty($sessionIndex)) {
+			$this->sessionIndex = $sessionIndex[0]->textContent;
+		}
+	}
+
+
+	/**
+	 * Retrieve the name identifier of the session that should be terminated.
+	 *
+	 * @return array  The name identifier of the session that should be terminated.
+	 */
+	public function getNameId() {
+		return $this->nameId;
+	}
+
+
+	/**
+	 * Set the name identifier of the session that should be terminated.
+	 *
+	 * The name identifier must be in the format accepted by SAML2_message::buildNameId().
+	 *
+	 * @see SAML2_message::buildNameId()
+	 * @param array $nameId  The name identifier of the session that should be terminated.
+	 */
+	public function setNameId($nameId) {
+		assert('is_array($nameId)');
+
+		$this->nameId = $nameId;
+	}
+
+
+	/**
+	 * Retrieve the sesion index of the session that should be terminated.
+	 *
+	 * @return string|NULL  The sesion index of the session that should be terminated.
+	 */
+	public function getSessionIndex() {
+		return $this->sessionIndex;
+	}
+
+
+	/**
+	 * Set the sesion index of the session that should be terminated.
+	 *
+	 * @param string|NULL $sessionIndex The sesion index of the session that should be terminated.
+	 */
+	public function setSessionIndex($sessionIndex) {
+		assert('is_string($sessionIndex)');
+
+		$this->sessionIndex = $sessionIndex;
+	}
+
+
+	/**
+	 * Convert this logout request message to an XML element.
+	 *
+	 * @return DOMElement  This logout request.
+	 */
+	public function toUnsignedXML() {
+
+		$root = parent::toUnsignedXML();
+
+		SAML2_Utils::addNameId($root, $this->nameId);
+
+		if ($this->sessionIndex !== NULL) {
+			$sessionIndex = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'SessionIndex');
+			$sessionIndex->appendChild($this->document->createTextNode($this->sessionIndex));
+			$root->appendChild($sessionIndex);
+		}
+
+		return $root;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/LogoutResponse.php b/lib/SAML2/LogoutResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..b242d679aeb55f5a1e47a360beb9e1d1902e391f
--- /dev/null
+++ b/lib/SAML2/LogoutResponse.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Class for SAML 2 LogoutResponse messages.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_LogoutResponse extends SAML2_StatusResponse {
+
+	/**
+	 * Constructor for SAML 2 response messages.
+	 *
+	 * @param string $tagName  The tag name of the root element.
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+		parent::__construct('LogoutResponse', $xml);
+
+		/* No new fields added by LogoutResponse. */
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Message.php b/lib/SAML2/Message.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a4c262b9feea60fbe9e9d17adcfd135489ff49b
--- /dev/null
+++ b/lib/SAML2/Message.php
@@ -0,0 +1,474 @@
+<?php
+
+/**
+ * Base class for all SAML 2 messages.
+ *
+ * Implements what is common between the samlp:RequestAbstractType and
+ * samlp:StatusResponseType element types.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class SAML2_Message implements SAML2_SignedElement {
+
+	/**
+	 * The name of the root element of the DOM tree for the message.
+	 *
+	 * Used when creating a DOM tree from the message.
+	 *
+	 * @var string
+	 */
+	private $tagName;
+
+
+	/**
+	 * The identifier of this message.
+	 *
+	 * @var string
+	 */
+	private $id;
+
+
+	/**
+	 * The issue timestamp of this message, as an UNIX timestamp.
+	 *
+	 * @var int
+	 */
+	private $issueInstant;
+
+
+	/**
+	 * The destination URL of this message if it is known.
+	 *
+	 * @var string|NULL
+	 */
+	private $destination;
+
+
+	/**
+	 * The entity id of the issuer of this message, or NULL if unknown.
+	 *
+	 * @var string|NULL
+	 */
+	private $issuer;
+
+
+	/**
+	 * The RelayState associated with this message.
+	 *
+	 * @var string|NULL
+	 */
+	private $relayState;
+
+
+	/**
+	 * The DOMDocument we are currently building.
+	 *
+	 * This variable is used while generating XML from this message. It holds the
+	 * DOMDocument of the XML we are generating.
+	 *
+	 * @var DOMDocument
+	 */
+	protected $document;
+
+
+	/**
+	 * The private key we should use to sign the message.
+	 *
+	 * The private key can be NULL, in which case the message is sent unsigned.
+	 *
+	 * @var XMLSecurityKey|NULL
+	 */
+	private $signatureKey;
+
+
+	/**
+	 * List of certificates that should be included in the message.
+	 *
+	 * @var array
+	 */
+	private $certificates;
+
+
+	/**
+	 * Available methods for validating this message.
+	 *
+	 * @var array
+	 */
+	private $validators;
+
+
+	/**
+	 * Initialize a message.
+	 *
+	 * This constructor takes an optional parameter with a DOMElement. If this
+	 * parameter is given, the message will be initialized with data from that
+	 * XML element.
+	 *
+	 * If no XML element is given, the message is initialized with suitable
+	 * default values.
+	 *
+	 * @param string $tagName  The tag name of the root element.
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	protected function __construct($tagName, DOMElement $xml = NULL) {
+		assert('is_string($tagName)');
+		$this->tagName = $tagName;
+
+		$this->id = SimpleSAML_Utilities::generateID();
+		$this->issueInstant = time();
+		$this->certificates = array();
+		$this->validators = array();
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		if (!$xml->hasAttribute('ID')) {
+			throw new Exception('Missing ID attribute on SAML message.');
+		}
+		$this->id = $xml->getAttribute('ID');
+
+		if ($xml->getAttribute('Version') !== '2.0') {
+			/* Currently a very strict check. */
+			throw new Exception('Unsupported version: ' . $xml->getAttribute('Version'));
+		}
+
+		$this->issueInstant = SimpleSAML_Utilities::parseSAML2Time($xml->getAttribute('IssueInstant'));
+
+		if ($xml->hasAttribute('Destination')) {
+			$this->destination = $xml->getAttribute('Destination');
+		}
+
+		$issuer = SAML2_Utils::xpQuery($xml, './saml:Issuer');
+		if (!empty($issuer)) {
+			$this->issuer = $issuer[0]->textContent;
+		}
+
+
+		/* Validate the signature element of the message. */
+		$sig = SAML2_Utils::validateElement($xml);
+		if ($sig !== FALSE) {
+			$this->certificates = $sig['Certificates'];
+			$this->validators[] = array(
+				'Function' => array('SAML2_Utils', 'validateSignature'),
+				'Data' => $sig,
+				);
+		}
+
+	}
+
+
+	/**
+	 * Add a method for validating this message.
+	 *
+	 * This function is used by the HTTP-Redirect binding, to make it possible to
+	 * check the signature against the one included in the query string.
+	 *
+	 * @param callback $function  The function which should be called.
+	 * @param mixed $data  The data that should be included as the first parameter to the function.
+	 */
+	public function addValidator($function, $data) {
+		assert('is_callable($function)');
+
+		$this->validators[] = array(
+			'Function' => $function,
+			'Data' => $data,
+			);
+	}
+
+
+	/**
+	 * Validate this message against a public key.
+	 *
+	 * TRUE is returned on success, FALSE is returned if we don't have any
+	 * signature we can validate. An exception is thrown if the signature
+	 * validation fails.
+	 *
+	 * @param XMLSecurityKey $key  The key we should check against.
+	 * @return boolean  TRUE on success, FALSE when we don't have a signature.
+	 */
+	public function validate(XMLSecurityKey $key) {
+
+		if (count($this->validators) === 0) {
+			return FALSE;
+		}
+
+		$exceptions = array();
+
+		foreach ($this->validators as $validator) {
+			$function = $validator['Function'];
+			$data = $validator['Data'];
+
+			try {
+				call_user_func($function, $data, $key);
+				/* We were able to validate the message with this validator. */
+				return TRUE;
+			} catch (Exception $e) {
+				$exceptions[] = $e;
+			}
+		}
+
+		/* No validators were able to validate the message. */
+		throw $exceptions[0];
+	}
+
+
+	/**
+	 * Retrieve the identifier of this message.
+	 *
+	 * @return string  The identifier of this message.
+	 */
+	public function getId() {
+		return $this->id;
+	}
+
+
+	/**
+	 * Set the identifier of this message.
+	 *
+	 * @param string $id  The new identifier of this message.
+	 */
+	public function setId($id) {
+		assert('is_string($id)');
+
+		$this->id = $id;
+	}
+
+
+	/**
+	 * Retrieve the issue timestamp of this message.
+	 *
+	 * @return int  The issue timestamp of this message, as an UNIX timestamp.
+	 */
+	public function getIssueInstant() {
+		return $this->issueInstant;
+	}
+
+
+	/**
+	 * Set the issue timestamp of this message.
+	 *
+	 * @param int $issueInstant  The new issue timestamp of this message, as an UNIX timestamp.
+	 */
+	public function setIssueInstant($issueInstant) {
+		assert('is_int($issueInstant)');
+
+		$this->issueInstant = $issueInstant;
+	}
+
+
+	/**
+	 * Retrieve the destination of this message.
+	 *
+	 * @return string|NULL  The destination of this message, or NULL if no destination is given.
+	 */
+	public function getDestination() {
+		return $this->destination;
+	}
+
+
+	/**
+	 * Set the destination of this message.
+	 *
+	 * @param string|NULL $destination  The new destination of this message.
+	 */
+	public function setDestination($destination) {
+		assert('is_string($destination) || is_null($destination)');
+
+		$this->destination = $destination;
+	}
+
+
+	/**
+	 * Retrieve the issuer if this message.
+	 *
+	 * @return string|NULL  The issuer of this message, or NULL if no issuer is given.
+	 */
+	public function getIssuer() {
+		return $this->issuer;
+	}
+
+
+	/**
+	 * Set the issuer of this message.
+	 *
+	 * @param string|NULL $issuer  The new issuer of this message.
+	 */
+	public function setIssuer($issuer) {
+		assert('is_string($issuer) || is_null($issuer)');
+
+		$this->issuer = $issuer;
+	}
+
+
+	/**
+	 * Retrieve the RelayState associated with this message.
+	 *
+	 * @return string|NULL  The RelayState, or NULL if no RelayState is given.
+	 */
+	public function getRelayState() {
+		return $this->relayState;
+	}
+
+
+	/**
+	 * Set the RelayState associated with this message.
+	 *
+	 * @param string|NULL $relayState  The new RelayState.
+	 */
+	public function setRelayState($relayState) {
+		assert('is_string($relayState) || is_null($relayState)');
+
+		$this->relayState = $relayState;
+	}
+
+
+	/**
+	 * Convert this message to an unsigned XML document.
+	 *
+	 * This method does not sign the resulting XML document.
+	 *
+	 * @return DOMElement  The root element of the DOM tree.
+	 */
+	public function toUnsignedXML() {
+
+		$this->document = new DOMDocument();
+
+		$root = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'samlp:' . $this->tagName);
+		$this->document->appendChild($root);
+
+		/* Ugly hack to add another namespace declaration to the root element. */
+		$root->setAttributeNS(SAML2_Const::NS_SAML, 'saml:tmp', 'tmp');
+		$root->removeAttributeNS(SAML2_Const::NS_SAML, 'tmp');
+
+		$root->setAttribute('ID', $this->id);
+		$root->setAttribute('Version', '2.0');
+		$root->setAttribute('IssueInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
+
+		if ($this->destination !== NULL) {
+			$root->setAttribute('Destination', $this->destination);
+		}
+
+		if ($this->issuer !== NULL) {
+			$issuer = $this->document->createElementNS(SAML2_Const::NS_SAML, 'saml:Issuer');
+			$issuer->appendChild($this->document->createTextNode($this->issuer));
+			$root->appendChild($issuer);
+		}
+
+		return $root;
+	}
+
+
+	/**
+	 * Convert this message to a signed XML document.
+	 *
+	 * This method sign the resulting XML document if the private key for
+	 * the signature is set.
+	 *
+	 * @return DOMElement  The root element of the DOM tree.
+	 */
+	public function toSignedXML() {
+
+		$root = $this->toUnsignedXML();
+
+		if ($this->signatureKey === NULL) {
+			/* We don't have a key to sign it with. */
+			return $root;
+		}
+
+
+		/* Find the position we should insert the signature node at. */
+		if ($this->issuer !== NULL) {
+			/*
+			 * We have an issuer node. The signature node should come
+			 * after the issuer node.
+			 */
+			$issuerNode = $root->firstChild;
+			$insertBefore = $issuerNode->nextSibling;
+		} else {
+			/* No issuer node - the signature element should be the first element. */
+			$insertBefore = $root->firstChild;
+		}
+
+
+		SAML2_Utils::insertSignature($this->signatureKey, $this->certificates, $root, $insertBefore);
+
+		return $root;
+	}
+
+
+	/**
+	 * Retrieve the private key we should use to sign the message.
+	 *
+	 * @return XMLSecurityKey|NULL The key, or NULL if no key is specified.
+	 */
+	public function getSignatureKey() {
+		return $this->signatureKey;
+	}
+
+
+	/**
+	 * Set the private key we should use to sign the message.
+	 *
+	 * If the key is NULL, the message will be sent unsigned.
+	 *
+	 * @param XMLSecurityKey|NULL $key
+	 */
+	public function setSignatureKey(XMLsecurityKey $signatureKey = NULL) {
+		$this->signatureKey = $signatureKey;
+	}
+
+
+	/**
+	 * Set the certificates that should be included in the message.
+	 *
+	 * The certificates should be strings with the PEM encoded data.
+	 *
+	 * @param array $certificates  An array of certificates.
+	 */
+	public function setCertificates(array $certificates) {
+		$this->certificates = $certificates;
+	}
+
+
+	/**
+	 * Retrieve the certificates that are included in the message.
+	 *
+	 * @return array  An array of certificates.
+	 */
+	public function getCertificates() {
+		return $this->certificates;
+	}
+
+
+	/**
+	 * Convert an XML element into a message.
+	 *
+	 * @param DOMElement $xml  The root XML element.
+	 * @return SAML2_Message  The message.
+	 */
+	public static function fromXML(DOMElement $xml) {
+
+		if ($xml->namespaceURI !== SAML2_Const::NS_SAMLP) {
+			throw new Exception('Unknown namespace of SAML message: ' . var_export($xml->namespaceURI, TRUE));
+		}
+
+		switch ($xml->localName) {
+		case 'AuthnRequest':
+			return new SAML2_AuthnRequest($xml);
+		case 'LogoutResponse':
+			return new SAML2_LogoutResponse($xml);
+		case 'LogoutRequest':
+			return new SAML2_LogoutRequest($xml);
+		case 'Response':
+			return new SAML2_Response($xml);
+		default:
+			throw new Exception('Unknown SAML message: ' . var_export($xml->localName, TRUE));
+		}
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Request.php b/lib/SAML2/Request.php
new file mode 100644
index 0000000000000000000000000000000000000000..a38f51f26cd455e0d43ed8af9c9de1380648d3ed
--- /dev/null
+++ b/lib/SAML2/Request.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Base class for all SAML 2 request messages.
+ *
+ * Implements samlp:RequestAbstractType. All of the elements in that type is
+ * stored in the SAML2_Message class, and this class is therefore empty. It
+ * is included mainly to make it easy to separate requests from responses.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class SAML2_Request extends SAML2_Message {
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Response.php b/lib/SAML2/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..d45c73f35e3aa1309107d8453e878381f86638cb
--- /dev/null
+++ b/lib/SAML2/Response.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Class for SAML 2 Response messages.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_Response extends SAML2_StatusResponse {
+
+	/**
+	 * The assertions in this response.
+	 */
+	private $assertions;
+
+
+	/**
+	 * Constructor for SAML 2 response messages.
+	 *
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	public function __construct(DOMElement $xml = NULL) {
+		parent::__construct('Response', $xml);
+
+		$this->assertions = array();
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		for ($node = $xml->firstChild; $node !== NULL; $node = $node->nextSibling) {
+			if ($node->namespaceURI !== SAML2_Const::NS_SAML) {
+				continue;
+			}
+
+			if ($node->localName === 'Assertion') {
+				$this->assertions[] = new SAML2_Assertion($node);
+			} elseif($node->localName === 'EncryptedAssertion') {
+				$this->assertions[] = new SAML2_EncryptedAssertion($node);
+			}
+		}
+	}
+
+
+	/**
+	 * Retrieve the assertions in this response.
+	 *
+	 * @return array  Array of SAML2_Assertion and SAML2_EncryptedAssertion objects.
+	 */
+	public function getAssertions() {
+		return $this->assertions;
+	}
+
+
+	/**
+	 * Set the assertions that should be included in this response.
+	 *
+	 * @param array  The assertions.
+	 */
+	public function setAssertions(array $assertions) {
+
+		$this->assertions = $assertions;
+	}
+
+
+	/**
+	 * Convert the response message to an XML element.
+	 *
+	 * @return DOMElement  This response.
+	 */
+	public function toUnsignedXML() {
+
+		$root = parent::toUnsignedXML();
+
+		foreach ($this->assertions as $assertion) {
+			$node = $assertion->toXML();
+			$node = $root->ownerDocument->importNode($node, TRUE);
+			$root->appendChild($node);
+		}
+
+		return $root;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/SignedElement.php b/lib/SAML2/SignedElement.php
new file mode 100644
index 0000000000000000000000000000000000000000..aef6eaf5c162d10eb46c32c5300e20f59a11bb84
--- /dev/null
+++ b/lib/SAML2/SignedElement.php
@@ -0,0 +1,31 @@
+<?php
+
+
+/**
+ * Interface to a SAML 2 element which may be signed.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+interface SAML2_SignedElement {
+
+	/**
+	 * Validate this element against a public key.
+	 *
+	 * If no signature is present, FALSE is returned. If a signature is present,
+	 * but cannot be verified, an exception will be thrown.
+	 *
+	 * @param XMLSecurityKey $key  The key we should check against.
+	 * @return boolean  TRUE if successful, FALSE if we don't have a signature that can be verified.
+	 */
+	public function validate(XMLSecurityKey $key);
+
+
+	/**
+	 * Retrieve the certificates that are included in the element (if any).
+	 *
+	 * @return array  An array of certificates.
+	 */
+	public function getCertificates();
+
+}
\ No newline at end of file
diff --git a/lib/SAML2/StatusResponse.php b/lib/SAML2/StatusResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..8093da30cb804b0ad8e957b449ecd30a0c18f004
--- /dev/null
+++ b/lib/SAML2/StatusResponse.php
@@ -0,0 +1,195 @@
+<?php
+
+/**
+ * Base class for all SAML 2 response messages.
+ *
+ * Implements samlp:StatusResponseType. All of the elements in that type is
+ * stored in the SAML2_Message class, and this class is therefore more
+ * or less empty. It is included mainly to make it easy to separate requests from
+ * responses.
+ *
+ * The status code is represented as an array on the following form:
+ * array(
+ *   'Code' => '<top-level status code>',
+ *   'SubCode' => '<second-level status code>',
+ *   'Message' => '<status message>',
+ * )
+ *
+ * Only the 'Code' field is required. The others will be set to NULL if they
+ * aren't present.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class SAML2_StatusResponse extends SAML2_Message {
+
+	/**
+	 * The ID of the request this is a response to, or NULL if this is an unsolicited response.
+	 *
+	 * @var string|NULL
+	 */
+	private $inResponseTo;
+
+
+	/**
+	 * The status code of the response.
+	 *
+	 * @var array
+	 */
+	private $status;
+
+
+	/**
+	 * Constructor for SAML 2 response messages.
+	 *
+	 * @param string $tagName  The tag name of the root element.
+	 * @param DOMElement|NULL $xml  The input message.
+	 */
+	protected function __construct($tagName, DOMElement $xml = NULL) {
+		parent::__construct($tagName, $xml);
+
+		$this->status = array(
+			'Code' => SAML2_Const::STATUS_SUCCESS,
+			'SubCode' => NULL,
+			'Message' => NULL,
+			);
+
+		if ($xml === NULL) {
+			return;
+		}
+
+		if ($xml->hasAttribute('InResponseTo')) {
+			$this->inResponseTo = $xml->getAttribute('InResponseTo');
+		}
+
+		$status = SAML2_Utils::xpQuery($xml, './samlp:Status');
+		if (empty($status)) {
+			throw new Exception('Missing status code on response.');
+		}
+		$status = $status[0];
+
+		$statusCode = SAML2_Utils::xpQuery($status, './samlp:StatusCode');
+		if (empty($statusCode)) {
+			throw new Exception('Missing status code in status element.');
+		}
+		$statusCode = $statusCode[0];
+
+		$this->status['Code'] = $statusCode->getAttribute('Value');
+
+		$subCode = SAML2_Utils::xpQuery($statusCode, './samlp:StatusCode');
+		if (!empty($subCode)) {
+			$this->status['SubCode'] = $subCode[0]->getAttribute('Value');
+		}
+
+		$message = SAML2_Utils::xpQuery($status, './samlp:StatusMessage');
+		if (!empty($message)) {
+			$this->status['Message'] = $message[0]->textContent;
+		}
+	}
+
+
+	/**
+	 * Determine whether this is a successful response.
+	 *
+	 * @return boolean  TRUE if the status code is success, FALSE if not.
+	 */
+	public function isSuccess() {
+		assert('array_key_exists("Code", $this->status)');
+
+		if ($this->status['Code'] === SAML2_Const::STATUS_SUCCESS) {
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+
+	/**
+	 * Retrieve the ID of the request this is a response to.
+	 *
+	 * @return string|NULL  The ID of the request.
+	 */
+	public function getInResponseTo() {
+		return $this->inResponseTo;
+	}
+
+
+	/**
+	 * Set the ID of the request this is a response to.
+	 *
+	 * @param string|NULL $inResponseTo  The ID of the request.
+	 */
+	public function setInResponseTo($inResponseTo) {
+		assert('is_string($inResponseTo) || is_null($inResponseTo)');
+
+		$this->inResponseTo = $inResponseTo;
+	}
+
+
+	/**
+	 * Retrieve the status code.
+	 *
+	 * @return array  The status code.
+	 */
+	public function getStatus() {
+		return $this->status;
+	}
+
+
+	/**
+	 * Set the status code.
+	 *
+	 * @param array $status  The status code.
+	 */
+	public function setStatus(array $status) {
+		assert('array_key_exists("Code", $status)');
+
+		$this->status = $status;
+		if (!array_key_exists('SubCode', $status)) {
+			$this->status['SubCode'] = NULL;
+		}
+		if (!array_key_exists('Message', $status)) {
+			$this->status['Message'] = NULL;
+		}
+	}
+
+
+	/**
+	 * Convert status response message to an XML element.
+	 *
+	 * @return DOMElement  This status response.
+	 */
+	public function toUnsignedXML() {
+
+		$root = parent::toUnsignedXML();
+
+		if ($this->inResponseTo !== NULL) {
+			$root->setAttribute('InResponseTo', $this->inResponseTo);
+		}
+
+		$status = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'Status');
+		$root->appendChild($status);
+
+		$statusCode = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'StatusCode');
+		$statusCode->setAttribute('Value', $this->status['Code']);
+		$status->appendChild($statusCode);
+
+		if (!is_null($this->status['SubCode'])) {
+			$subStatusCode = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'StatusCode');
+			$subStatusCode->setAttribute('Value', $this->status['SubCode']);
+			$statusCode->appendChild($subStatusCode);
+		}
+
+		if (!is_null($this->status['Message'])) {
+			$statusMessage = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'StatusMessage');
+			$statusMessage->appendChild($this->document->createTextNode($this->status['Message']));
+			$status->appendChild($statusMessage);
+		}
+
+		return $root;
+	}
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/SAML2/Utils.php b/lib/SAML2/Utils.php
new file mode 100644
index 0000000000000000000000000000000000000000..164c31a1e62b6a517cb5e95224400b60c401eb06
--- /dev/null
+++ b/lib/SAML2/Utils.php
@@ -0,0 +1,244 @@
+<?php
+
+/**
+ * Helper functions for the SAML2 library.
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_Utils {
+
+	/**
+	 * Check the Signature in a XML element.
+	 *
+	 * This function expects the XML element to contain a Signature-element
+	 * which contains a reference to the XML-element. This is common for both
+	 * messages and assertions.
+	 *
+	 * Note that this function only validates the element itself. It does not
+	 * check this against any local keys.
+	 *
+	 * If no Signature-element is located, this function will return FALSE. All
+	 * other validation errors result in an exception. On successful validation
+	 * an array will be returned. This array contains the information required to
+	 * check the signature against a public key.
+	 *
+	 * @param DOMElement $root  The element which should be validated.
+	 * @return array|FALSE  An array with information about the Signature-element.
+	 */
+	public static function validateElement(DOMElement $root) {
+
+		/* Create an XML security object. */
+		$objXMLSecDSig = new XMLSecurityDSig();
+
+		/* Both SAML messages and SAML assertions use the 'ID' attribute. */
+		$objXMLSecDSig->idKeys[] = 'ID';
+
+		/* Locate the XMLDSig Signature element to be used. */
+		$signatureElement = self::xpQuery($root, './ds:Signature');
+		if (count($signatureElement) === 0) {
+			/* We don't have a signature element ot validate. */
+			return FALSE;
+		} elseif (count($signatureElement) > 1) {
+			throw new Exception('XMLSec: more than one signature element in root.');
+		}
+		$signatureElement = $signatureElement[0];
+		$objXMLSecDSig->sigNode = $signatureElement;
+
+		/* Canonicalize the XMLDSig SignedInfo element in the message. */
+		$objXMLSecDSig->canonicalizeSignedInfo();
+
+		/* Validate referenced xml nodes. */
+		if (!$objXMLSecDSig->validateReference()) {
+			throw new Exception('XMLsec: digest validation failed');
+		}
+
+		/* Check that $root is one of the signed nodes. */
+		$rootSigned = FALSE;
+		foreach ($objXMLSecDSig->getValidatedNodes() as $signedNode) {
+			if ($signedNode->isSameNode($root)) {
+				$rootSigned = TRUE;
+				break;
+			}
+		}
+		if (!$rootSigned) {
+			throw new Exception('XMLSec: The root element is not signed.');
+		}
+
+		/* Now we extract all available X509 certificates in the signature element. */
+		$certificates = array();
+		foreach (self::xpQuery($signatureElement, './ds:KeyInfo/ds:X509Data/ds:X509Certificate') as $certNode) {
+			$certData = $certNode->textContent;
+			$certData = str_replace(array("\r", "\n", "\t", ' '), '', $certData);
+			$certificates[] = $certData;
+		}
+
+		$ret = array(
+			'Signature' => $objXMLSecDSig,
+			'Certificates' => $certificates,
+			);
+
+		return $ret;
+	}
+
+
+	/**
+	 * Check a signature against a key.
+	 *
+	 * An exception is thrown if we are unable to validate the signature.
+	 *
+	 * @param array $info  The information returned by the validateElement()-function.
+	 * @param XMLSecurityKey $key  The publickey that should validate the Signature object.
+	 */
+	public static function validateSignature(array $info, XMLSecurityKey $key) {
+		assert('array_key_exists("Signature", $info)');
+
+		$objXMLSecDSig = $info['Signature'];
+
+		/* Check the signature. */
+		if (! $objXMLSecDSig->verify($key)) {
+			throw new Exception("Unable to validate Signature");
+		}
+	}
+
+
+	/**
+	 * Do an XPath query on an XML node.
+	 *
+	 * @param DOMNode $node  The XML node.
+	 * @param string $query  The query.
+	 * @return array  Array with matching DOM nodes.
+	 */
+	public static function xpQuery(DOMNode $node, $query) {
+		assert('is_string($query)');
+		static $xpCache = NULL;
+
+		if ($xpCache === NULL || !$xpCache->document->isSameNode($node->ownerDocument)) {
+			$xpCache = new DOMXPath($node->ownerDocument);
+			$xpCache->registerNamespace('samlp', SAML2_Const::NS_SAMLP);
+			$xpCache->registerNamespace('saml', SAML2_Const::NS_SAML);
+			$xpCache->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS);
+			$xpCache->registerNamespace('xenc', XMLSecEnc::XMLENCNS);
+		}
+
+		$results = $xpCache->query($query, $node);
+		$ret = array();
+		for ($i = 0; $i < $results->length; $i++) {
+			$ret[$i] = $results->item($i);
+		}
+
+		return $ret;
+	}
+
+
+	/**
+	 * Parse a boolean attribute.
+	 *
+	 * @param DOMElement $node  The element we should fetch the attribute from.
+	 * @param string $attributeName  The name of the attribute.
+	 * @param mixed $default  The value that should be returned if the attribute doesn't exist.
+	 * @return bool|mixed  The value of the attribute, or $default if the attribute doesn't exist.
+	 */
+	public static function parseBoolean(DOMElement $node, $attributeName, $default = NULL) {
+		assert('is_string($attributeName)');
+
+		if (!$node->hasAttribute($attributeName)) {
+			return $default;
+		}
+		$value = $node->getAttribute($attributeName);
+		switch (strtolower($value)) {
+		case '0':
+		case 'false':
+			return FALSE;
+		case '1':
+		case 'true':
+			return TRUE;
+		default:
+			throw new Exception('Invalid value of boolean attribute ' . var_export($attributeName, TRUE) . ': ' . var_export($value, TRUE));
+		}
+	}
+
+
+	/**
+	 * Create a NameID element.
+	 *
+	 * The NameId array can have the following elements: 'Value', 'Format',
+	 *   'NameQualifier, 'SPNameQualifier'
+	 *
+	 * Only the 'Value'-element is required.
+	 *
+	 * @param DOMElement $node  The DOM node we should append the NameId to.
+	 * @param array $nameId  The name identifier.
+	 */
+	public static function addNameId(DOMElement $node, array $nameId) {
+		assert('array_key_exists("Value", $nameId)');
+
+		$xml = $node->ownerDocument->createElementNS(SAML2_Const::NS_SAML, 'saml:NameID');
+		$node->appendChild($xml);
+
+		if (array_key_exists('NameQualifier', $nameId)) {
+			$xml->setAttribute('NameQualifier', $nameId['NameQualifier']);
+		}
+		if (array_key_exists('SPNameQualifier', $nameId)) {
+			$xml->setAttribute('SPNameQualifier', $nameId['SPNameQualifier']);
+		}
+		if (array_key_exists('Format', $nameId)) {
+			$xml->setAttribute('Format', $nameId['Format']);
+		}
+
+		$xml->appendChild($node->ownerDocument->createTextNode($nameId['Value']));
+	}
+
+
+	/**
+	 * Parse a NameID element.
+	 *
+	 * @param DOMElement $xml  The DOM element we should parse.
+	 * @return array  The parsed name identifier.
+	 */
+	public static function parseNameId(DOMElement $xml) {
+
+		$ret = array('Value' => $xml->textContent);
+
+		foreach (array('NameQualifier', 'SPNameQualifier', 'Format') as $attr) {
+			if ($xml->hasAttribute($attr)) {
+				$ret[$attr] = $xml->getAttribute($attr);
+			}
+		}
+
+		return $ret;
+	}
+
+
+	/**
+	 * Insert a Signature-node.
+	 *
+	 * @param XMLSecurityKey $key  The key we should use to sign the message.
+	 * @param array $certificates  The certificates we should add to the signature node.
+	 * @param DOMElement $root  The XML node we should sign.
+	 * @param DomElement $insertBefore  The XML element we should insert the signature element before.
+	 */
+	public static function insertSignature(XMLSecurityKey $key, array $certificates, DOMElement $root, DOMNode $insertBefore = NULL) {
+
+		$objXMLSecDSig = new XMLSecurityDSig();
+		$objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
+
+		$objXMLSecDSig->addReferenceList(
+			array($root),
+			XMLSecurityDSig::SHA1,
+			array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
+			array('id_name' => 'ID')
+			);
+
+		$objXMLSecDSig->sign($key);
+
+		foreach ($certificates as $certificate) {
+			$objXMLSecDSig->add509Cert($certificate, TRUE);
+		}
+
+		$objXMLSecDSig->insertSignature($root, $insertBefore);
+
+	}
+}
+
+?>
\ No newline at end of file