diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php
new file mode 100644
index 0000000000000000000000000000000000000000..c0a47057d451bc73cf4e55637648f02f94f84059
--- /dev/null
+++ b/lib/SimpleSAML/Memcache.php
@@ -0,0 +1,418 @@
+<?php
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Configuration.php');
+
+/**
+ * This file implements functions to read and write to a group of memcache
+ * servers.
+ *
+ * The goals of this storage class is to provide failover, redudancy and load
+ * balancing. This is accomplished by storing the data object to several
+ * groups of memcache servers. Each data object is replicated to every group
+ * of memcache servers, but it is only stored to one server in each group.
+ *
+ * For this code to work correctly, all web servers accessing the data must
+ * have the same clock (as measured by the time()-function). Different clock
+ * values will lead to incorrect behaviour.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SimpleSAML_Memcache {
+
+	/**
+	 * Cache of the memcache servers we are using.
+	 */
+	private static $serverGroups = NULL;
+
+
+	/**
+	 * Find data stored with a given key.
+	 *
+	 * @param $key  The key of the data.
+	 * @return The data stored with the given key, or NULL if no data matching the key was found.
+	 */
+	public static function get($key) {
+
+		$latestTime = 0.0;
+		$latestData = NULL;
+		$mustUpdate = FALSE;
+
+		/* Search all the servers for the given id. */
+		foreach(self::getMemcacheServers() as $server) {
+			$serializedInfo = $server->get($key);
+			if($serializedInfo === FALSE) {
+				/* Either the server is down, or we don't have the value stored on that server. */
+				$mustUpdate = TRUE;
+				continue;
+			}
+
+			/* Deserialize the object. */
+			$info = unserialize($serializedInfo);
+
+			/*
+			 * Make sure that this is an array with two keys:
+			 * - 'timestamp': The time the data was saved.
+			 * - 'data': The data.
+			 */
+			if(!is_array($info)) {
+				error_log('Retrieved invalid data from a memcache server.' .
+					' Data was not an array.');
+				continue;
+			}
+			if(!array_key_exists('timestamp', $info)) {
+				error_log('Retrieved invalid data from a memcache server.' .
+					' Missing timestamp.');
+				continue;
+			}
+			if(!array_key_exists('data', $info)) {
+				error_log('Retrieved invalid data from a memcache server.' .
+					' Missing data.');
+				continue;
+			}
+
+
+			if($latestTime === 0.0) {
+				/* First data found. */
+				$latestTime = $info['timestamp'];
+				$latestData = $info['data'];
+				continue;
+			}
+
+			if($info['timestamp'] === $latestTime && $info['data'] === $latestData) {
+				/* This data matches the data from the other server(s). */
+				continue;
+			}
+
+			/*
+			 * Different data from different servers. We need to update at least one
+			 * of them to maintain synchronization.
+			 */
+
+			$mustUpdate = TRUE;
+
+			/* Update if data in $info is newer than $latestData. */
+			if($latestTime < $info['timestamp']) {
+				$latestTime = $info['timestamp'];
+				$latestData = $info['data'];
+			}
+		}
+
+		if($latestTime === 0.0) {
+			/* We didn't find any data matching the key. */
+			return NULL;
+		}
+
+		if($mustUpdate) {
+			/* We found data matching the key, but some of the servers need updating. */
+			self::set($key, $latestData);
+		}
+
+		return $latestData;
+	}
+
+
+	/**
+	 * Save a key-value pair to the memcache servers.
+	 *
+	 * @param $key    The key of the data.
+	 * @param $value  The value of the data.
+	 */
+	public static function set($key, $value) {
+		$savedInfo = array(
+			'timestamp' => microtime(TRUE),
+			'data' => $value
+			);
+
+		$savedInfoSerialized = serialize($savedInfo);
+
+		/* Store this object to all groups of memcache servers. */
+		foreach(self::getMemcacheServers() as $server) {
+			$server->set($key, $savedInfoSerialized, 0, self::getExpireTime());
+		}
+	}
+
+
+	/**
+	 * This function adds a server from the 'memcache_store.servers'
+	 * configuration option to a Memcache object.
+	 *
+	 * The server parameter is an array with the following keys:
+	 *  - hostname
+	 *    Hostname or ip address to the memcache server.
+	 *  - port (optional)
+	 *    port number the memcache server is running on. This
+	 *    defaults to memcache.default_port if no value is given.
+	 *    The default value of memcache.default_port is 11211.
+	 *  - weight (optional)
+	 *    The weight of this server in the load balancing
+	 *    cluster.
+	 *  - timeout (optional)
+	 *    The timeout for contacting this server, in seconds.
+	 *    The default value is 3 seconds.
+	 *
+	 * @param $memcache  The Memcache object we should add this server to.
+	 * @param $server    The server we should add.
+	 */
+	private static function addMemcacheServer($memcache, $server) {
+
+		/* The hostname option is required. */
+		if(!array_key_exists('hostname', $server)) {
+			throw new Exception('hostname setting missing from server in the' .
+				' \'memcache_store.servers\' configuration option.');
+		}
+
+		$hostname = $server['hostname'];
+
+		/* The hostname must be a valid string. */
+		if(!is_string($hostname)) {
+			throw new Exception('Invalid hostname for server in the' .
+				' \'memcache_store.servers\' configuration option. The hostname is' .
+				' supposed to be a string.');
+		}
+
+		/* Check if the user has specified a port number. */
+		if(array_key_exists('port', $server)) {
+			/* Get the port number from the array, and validate it. */
+			$port = (int)$server['port'];
+			if(($port <= 0) || ($port > 65535)) {
+				throw new Exception('Invalid port for server in the' .
+					' \'memcache_store.servers\' configuration option. The port number' .
+					' is supposed to be an integer between 0 and 65535.');
+			}
+		} else {
+			/* Use the default port number from the ini-file. */
+			$port = (int)ini_get('memcache.default_port');
+			if($port <= 0 || $port > 65535) {
+				/* Invalid port number from the ini-file. fall back to the default. */
+				$port = 11211;
+			}
+		}
+
+		/* Check if the user has specified a weight for this server. */
+		if(array_key_exists('weight', $server)) {
+			/* Get the weight and validate it. */
+			$weight = (int)$server['weight'];
+			if($weight <= 0) {
+				throw new Exception('Invalid weight for server in the' .
+					' \'memcache_store.servers\' configuration option. The weight is' .
+					' supposed to be a positive integer.');
+			}
+		} else {
+			/* Use a default weight of 1.  */
+			$weight = 1;
+		}
+
+		/* Check if the user has specified a timeout for this server. */
+		if(array_key_exists('timeout', $server)) {
+			/* Get the timeout and validate it. */
+			$timeout = (int)$server['timeout'];
+			if($timeout <= 0) {
+				throw new Exception('Invalid timeout for server in the' .
+					' \'memcache_store.servers\' configuration option. The timeout is' .
+					' supposed to be a positive integer.');
+			}
+		} else {
+			/* Use a default timeout of 3 seconds. */
+			$timeout = 3;
+		}
+
+		/* Add this server to the Memcache object. */
+		$memcache->addServer($hostname, $port, TRUE, $weight, $timeout);
+	}
+
+
+	/**
+	 * This function takes in a list of servers belonging to a group and
+	 * creates a Memcache object from the servers in the group.
+	 *
+	 * @param $group  Array of servers which should be created as a group.
+	 * @return A Memcache object of the servers in the group.
+	 */
+	private static function loadMemcacheServerGroup($group) {
+		/* Create the Memcache object. */
+		$memcache = new Memcache();
+		if($memcache == NULL) {
+			throw new Exception('Unable to create an instance of a Memcache object.' .
+				' Is the memcache extension installed?');
+		}
+
+		/* Iterate over all the servers in the group and add them to the Memcache object. */
+		foreach($group as $index => $server) {
+			/*
+			 * Make sure that we don't have an index. An index would be a sign of invalid configuration.
+			 */
+			if(!is_int($index)) {
+				throw new Exception('Invalid index on element in the' .
+					' \'memcache_store.servers\' configuration option. Perhaps you' .
+					' have forgotten to add an array(...) around one of the server groups?' .
+					' The invalid index was: ' . $index);
+			}
+
+			/*
+			 * Make sure that the server object is an array. Each server is an array with
+			 * name-value pairs.
+			 */
+			if(!is_array($server)) {
+				throw new Exception('Invalid value for the server with index ' . $index .
+					'. Remeber that the \'memcache_store.servers\' configuration option' .
+					' contains an array of arrays of arrays.');
+			}
+
+			self::addMemcacheServer($memcache, $server);
+		}
+
+		return $memcache;
+	}
+
+
+	/**
+	 * This function gets a list of all configured memcache servers. This list is initialized based
+	 * on the content of 'memcache_store.servers' in the configuration.
+	 *
+	 * @return Array with Memcache objects.
+	 */
+	private static function getMemcacheServers() {
+
+		/* Check if we have loaded the servers already. */
+		if(self::$serverGroups != NULL) {
+			return self::$serverGroups;
+		}
+
+		/* Initialize the servers-array. */
+		self::$serverGroups = array();
+
+		/* Load the configuration. */
+		$config = SimpleSAML_Configuration::getInstance();
+		assert($config instanceof SimpleSAML_Configuration);
+
+
+		$groups = $config->getValue('memcache_store.servers');
+
+		/* Validate the 'memcache_store.servers' configuration option. */
+		if(is_null($groups)) {
+			throw new Exception('Unable to get value of the \'memcache_store.servers\'' .
+				' configuration option.');
+		}
+		if(!is_array($groups)) {
+			throw new Exception('The value of the \'memcache_store.servers\' configuration' .
+				' option is supposed to be an array.');
+		}
+
+		/* Iterate over all the groups in the 'memcache_store.servers' configuration option. */
+		foreach($groups as $index => $group) {
+			/*
+			 * Make sure that the group doesn't have an index. An index would be a sign of
+			 * invalid configuration.
+			 */
+			if(!is_int($index)) {
+				throw new Exception('Invalid index on element in the \'memcache_store.servers\'' .
+					' configuration option. Perhaps you have forgotten to add an array(...)' .
+					' around one of the server groups? The invalid index was: ' . $index);
+			}
+
+			/*
+			 * Make sure that the group is an array. Each group is an array of servers. Each server is
+			 * an array of name => value pairs for that server.
+			 */
+			if(!is_array($group)) {
+				throw new Exception('Invalid value for the server with index ' . $index .
+					'. Remeber that the \'memcache_store.servers\' configuration option' .
+					' contains an array of arrays of arrays.');
+			}
+
+			/* Parse and add this group to the server group list. */
+			self::$serverGroups[] = self::loadMemcacheServerGroup($group);
+		}
+
+		return self::$serverGroups;
+	}
+
+
+	/**
+	 * This is a helper-function which returns the expire value of data
+	 * we should store to the memcache servers.
+	 *
+	 * The value is set depending on the configuration. If no value is
+	 * set in the configuration, then we will use a default value of 0.
+	 * 0 means that the item will never expire.
+	 *
+	 * @return  The value which should be passed in the set(...) calls to the memcache objects.
+	 */
+	private static function getExpireTime()
+	{
+		/* Get the configuration instance. */
+		$config = SimpleSAML_Configuration::getInstance();
+		assert($config instanceof SimpleSAML_Configuration);
+
+		/* Get the expire-value from the configuration. */
+		$expire = $config->getValue('memcache_store.expires');
+
+		/* If 'memcache_store.expires' isn't defined in the
+		 * configuration, then we will use 0 as the expire parameter.
+		 */
+		if($expire === NULL) {
+			return 0;
+		}
+
+		/* The 'memcache_store.expires' option must be an integer. */
+		if(!is_integer($expire)) {
+			$e = 'The value of \'memcache_store.expires\' in the' .
+			     ' configuration must be a valid integer.';
+			error_log($e);
+			die($e);
+		}
+
+		/* It must be a positive integer. */
+		if($expire < 0) {
+			$e = 'The value of \'memcache_store.expires\' in the' .
+			     ' configuration can\'t be a negative integer.';
+			error_log($e);
+			die($e);
+		}
+
+		/* If the configuration option is 0, then we should
+		 * return 0. This allows the user to specify that the data
+		 * shouldn't expire.
+		 */
+		if($expire == 0) {
+			return 0;
+		}
+
+		/* The expire option is given as the number of seconds into the
+		 * future an item should expire. We convert this to an actual
+		 * timestamp.
+		 */
+		$expireTime = time() + $expire;
+
+		return $expireTime;
+	}
+
+
+	/**
+	 * This function retrieves statistics about all memcache server groups.
+	 *
+	 * @return Array with the names of each stat and an array with the value for each
+	 *         server group.
+	 */
+	public static function getStats()
+	{
+		$ret = array();
+
+		foreach(self::getMemcacheServers() as $sg) {
+			$stats = $sg->getExtendedStats();
+			if($stats === FALSE) {
+				throw new Exception('Failed to get memcache server status.');
+			}
+
+			$stats = SimpleSAML_Utilities::transposeArray($stats);
+
+			$ret = array_merge_recursive($ret, $stats);
+		}
+
+		return $ret;
+	}
+
+}
+?>
\ No newline at end of file