From: Michael M Slusarz Date: Fri, 14 May 2010 23:30:14 +0000 (-0600) Subject: SessionHandler rewrite. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=27f3db208654339614b2fced9677d55729eb37eb;p=horde.git SessionHandler rewrite. * Abstracted memcache persistent-backend code into 'Stack' driver. * Renamed 'none' driver to 'Builtin'. * Now throws Horde_SessionHandler_Exception. * Split driver code into abstract class. * Use horde/Db to access SQL databases. * Do not rely on Horde::logMessage() (or any of Core). --- diff --git a/framework/Core/lib/Horde/Core/Binder/SessionHandler.php b/framework/Core/lib/Horde/Core/Binder/SessionHandler.php index f554f5bba..c489cf946 100644 --- a/framework/Core/lib/Horde/Core/Binder/SessionHandler.php +++ b/framework/Core/lib/Horde/Core/Binder/SessionHandler.php @@ -9,29 +9,47 @@ class Horde_Core_Binder_SessionHandler implements Horde_Injector_Binder { global $conf; - $driver = empty($conf['sessionhandler']['type']) - ? 'None' - : $conf['sessionhandler']['type']; - + if (empty($conf['sessionhandler']['type'])) { + $driver = 'Builtin'; + } else { + $driver = $conf['sessionhandler']['type']; + if (strcasecmp($driver, 'None')) { + $driver = 'Builtin'; + } + } $params = Horde::getDriverConfig('sessionhandler', $driver); if (strcasecmp($driver, 'Sql') === 0) { - $write_db = $injector->getInstance('Horde_Db_Pear')->getOb(); - - /* Check if we need to set up the read DB connection - * separately. */ - if (empty($params['splitread'])) { - $params['db'] = $write_db; - } else { - $params['write_db'] = $write_db; - $params['db'] = $injector->getInstance('Horde_Db_Pear')->getOb('read'); - } + $params['db'] = $injector->getInstance('Horde_Db_Adapter_Base'); } - if (!empty($conf['sessionhandler']['memcache'])) { - $params['memcache'] = $injector->getInstance('Horde_Memcache'); + $logger = $injector->getInstance('Horde_Log_Logger'); + + if (!empty($conf['sessionhandler']['memcache']) && + (strcasecmp($driver, 'Builtin') != 0) && + (strcasecmp($driver, 'Memcache') != 0)) { + $params = array( + 'stack' => array( + array( + 'driver' => 'Memcache', + 'params' => array( + 'memcache' => $injector->getInstance('Horde_Memcache'), + 'logger' => $logger + ) + ), + array( + 'driver' => $driver, + 'params' => array_merge($params, array( + 'logger' => $logger + )) + ) + ) + ); + $driver = 'Stack'; } + $params['logger'] = $logger; + return Horde_SessionHandler::factory($driver, $params); } diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php index fa81333f3..d4f6fac36 100644 --- a/framework/Core/lib/Horde/Registry.php +++ b/framework/Core/lib/Horde/Registry.php @@ -1539,33 +1539,23 @@ class Horde_Registry if (!empty($conf['session']['use_only_cookies'])) { ini_set('session.use_only_cookies', 1); if (!empty($conf['cookie']['domain']) && - strpos($conf['server']['name'], '.') === false) { + (strpos($conf['server']['name'], '.') === false)) { throw new Horde_Exception('Session cookies will not work without a FQDN and with a non-empty cookie domain. Either use a fully qualified domain name like "http://www.example.com" instead of "http://example" only, or set the cookie domain in the Horde configuration to an empty value, or enable non-cookie (url-based) sessions in the Horde configuration.'); } } - session_set_cookie_params($conf['session']['timeout'], - $conf['cookie']['path'], $conf['cookie']['domain'], $conf['use_ssl'] == 1 ? 1 : 0); + session_set_cookie_params( + $conf['session']['timeout'], + $conf['cookie']['path'], + $conf['cookie']['domain'], + $conf['use_ssl'] == 1 ? 1 : 0 + ); session_cache_limiter(is_null($this->initParams['session_cache_limiter']) ? $conf['session']['cache_limiter'] : $this->initParams['session_cache_limiter']); session_name(urlencode($conf['session']['name'])); - $type = empty($conf['sessionhandler']['type']) - ? 'none' - : $conf['sessionhandler']['type']; - - if ($type == 'external') { - $calls = $conf['sessionhandler']['params']; - session_set_save_handler( - $calls['open'], - $calls['close'], - $calls['read'], - $calls['write'], - $calls['destroy'], - $calls['gc'] - ); - } elseif ($type != 'none') { - $this->sessionHandler = $GLOBALS['injector']->getInstance('Horde_SessionHandler'); - } + /* We want to create an instance here, not get, since we may be + * destroying the previous instances in the page. */ + $this->sessionHandler = $GLOBALS['injector']->createInstance('Horde_SessionHandler'); } /** diff --git a/framework/SessionHandler/lib/Horde/SessionHandler.php b/framework/SessionHandler/lib/Horde/SessionHandler.php index a52ecdadc..ce26e01f1 100644 --- a/framework/SessionHandler/lib/Horde/SessionHandler.php +++ b/framework/SessionHandler/lib/Horde/SessionHandler.php @@ -3,354 +3,40 @@ * Horde_SessionHandler:: defines an API for implementing custom PHP session * handlers. * - * Optional parameters:
- * 'memcache' - (Horde_Memcache) If set, uses memcache to cache session
- *              data.
- * 
- * * Copyright 2002-2010 The Horde Project (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * - * @author Mike Cochrane - * @author Michael Slusarz - * @package Horde_SessionHandler + * @author Mike Cochrane + * @author Michael Slusarz + * @category Horde + * @package SessionHandler */ class Horde_SessionHandler { /** - * Hash containing connection parameters. - * - * @var array - */ - protected $_params = array(); - - /** - * Initial session data signature. - * - * @var string - */ - protected $_sig; - - /** - * Force saving the session data? - * - * @var boolean - */ - protected $_force = false; - - /** - * Has a connection been made to the backend? - * - * @var boolean - */ - protected $_connected = false; - - /** - * Attempts to return a concrete Horde_SessionHandler instance based on - * $driver. + * Attempts to return a concrete instance based on $driver. * * @param string $driver The type of concrete subclass to return * (case-insensitive). * @param array $params A hash containing any additional configuration or * connection parameters a subclass might need. * - * @return Horde_SessionHandler The newly created concrete instance. - * @throws Horde_Exception + * @return Horde_SessionHandler_Driver The newly created concrete + * instance. + * @throws Horde_SessionHandler_Exception */ - static public function factory($driver, $params = array()) + static public function factory($driver, array $params = array()) { $driver = basename(strtolower($driver)); - $persistent_params = array(); - - if ($driver == 'memcached') { - // Trap for old driver name. - $driver = 'memcache'; - } elseif (($driver != 'memcache') && !empty($params['memcache'])) { - $p_params = $params; - unset($p_params['memcache']); - - $persistent_params = array( - 'persistent_driver' => $driver, - 'persistent_params' => $p_params - ); - - $driver = 'memcache'; - } - $class = __CLASS__ . '_' . ucfirst($driver); if (class_exists($class)) { - if (empty($params)) { - $params = Horde::getDriverConfig('sessionhandler', $driver); - } - return new $class(array_merge($params, $persistent_params)); - } - - throw new Horde_Exception('Driver "' . $driver . '" not found.'); - } - - /** - * Constructor. - * - * @param array $params A hash containing connection parameters. - * - * @throws Horde_Exception - */ - protected function __construct($params = array()) - { - $this->_params = $params; - - register_shutdown_function(array($this, 'shutdown')); - - ini_set('session.save_handler', 'user'); - session_set_save_handler( - array($this, 'open'), - array($this, 'close'), - array($this, 'read'), - array($this, 'write'), - array($this, 'destroy'), - array($this, 'gc') - ); - } - - /** - * Destructor. - */ - public function __destruct() - { - /* This is necessary as of PHP 5.0.5 because objects are not available - * when the write() handler is called at the end of a session - * access. */ - session_write_close(); - } - - /** - * Shutdown function. - * - * Used to determine if we need to write the session to avoid a session - * timeout, even though the session is unchanged. - * Theory: On initial login, set the current time plus half of the max - * lifetime in the session. Then check this timestamp before saving. - * If we exceed, force a write of the session and set a new timestamp. - * Why half the maxlifetime? It guarantees that if we are accessing the - * server via a periodic mechanism (think folder refreshing in IMP) that - * we will catch this refresh. - */ - public function shutdown() - { - $curr_time = time(); - - if (!isset($_SESSION['sessionhandler']) || - ($curr_time >= $_SESSION['sessionhandler'])) { - $_SESSION['sessionhandler'] = $curr_time + (ini_get('session.gc_maxlifetime') / 2); - $this->_force = true; - } - } - - /** - * Open the backend. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @return boolean True on success, false otherwise. - */ - public function open($save_path = null, $session_name = null) - { - if (!$this->_connected) { - try { - $this->_open($save_path, $session_name); - } catch (Horde_Exception $e) { - Horde::logMessage($e, 'ERR'); - return false; - } - - $this->_connected = true; - } - - return true; - } - - /** - * Open the backend. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @throws Horde_Exception - */ - protected function _open($save_path = null, $session_name = null) - { - } - - /** - * Close the backend. - * - * @return boolean True on success, false otherwise. - */ - public function close() - { - try { - $this->_close(); - } catch (Horde_Exception $e) { - Horde::logMessage($e, 'ERR'); - return false; - } - - $this->_connected = false; - return true; - } - - /** - * Close the backend. - * - * @throws Horde_Exception - */ - protected function _close() - { - } - - /** - * Read the data for a particular session identifier from the backend. - * This method should only be called internally by PHP via - * session_set_save_handler(). - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - public function read($id) - { - $result = $this->_read($id); - $this->_sig = md5($result); - return $result; - } - - /** - * Read the data for a particular session identifier from the backend. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _read($id) - { - return ''; - } - - /** - * Write session data to the backend. - * This method should only be called internally by PHP via - * session_set_save_handler(). - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - public function write($id, $session_data) - { - if (!$this->_force && ($this->_sig == md5($session_data))) { - Horde::logMessage('Session data unchanged (id = ' . $id . ')', 'DEBUG'); - return true; - } - - return $this->_write($id, $session_data); - } - - /** - * Write session data to the backend. - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - protected function _write($id, $session_data) - { - return false; - } - - /** - * Destroy the data for a particular session identifier in the backend. - * This method should only be called internally by PHP via - * session_set_save_handler(). - * - * @param string $id The session identifier. - * - * @return boolean True on success, false otherwise. - */ - public function destroy($id) - { - return false; - } - - /** - * Garbage collect stale sessions from the backend. - * This method should only be called internally by PHP via - * session_set_save_handler(). - * - * @param integer $maxlifetime The maximum age of a session. - * - * @return boolean True on success, false otherwise. - */ - public function gc($maxlifetime = 300) - { - return false; - } - - /** - * Get session data read-only. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _readOnly($id) - { - return $this->read($id); - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - * @throws Horde_Exception - */ - public function getSessionIDs() - { - throw new Horde_Exception(_("Not supported.")); - } - - /** - * Returns a list of authenticated users and data about their session. - * - * @return array For authenticated users, the sessionid as a key and the - * information returned from Horde_Auth::readSessionData() - * as values. - * @throws Horde_Exception - */ - public function getSessionsInfo() - { - $sessions = $this->getSessionIDs(); - - $info = array(); - - foreach ($sessions as $id) { - try { - $data = $this->_readOnly($id); - } catch (Horde_Exception $e) { - continue; - } - $data = Horde_Auth::readSessionData($data, true); - if ($data !== false) { - $info[$id] = $data; - } + return new $class($params); } - return $info; + throw new Horde_SessionHandler_Exception('Driver not found: ' . $driver); } } diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Builtin.php b/framework/SessionHandler/lib/Horde/SessionHandler/Builtin.php new file mode 100644 index 000000000..c0634bbfe --- /dev/null +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Builtin.php @@ -0,0 +1,135 @@ + + * @category Horde + * @package SessionHandler + */ +class Horde_SessionHandler_Builtin extends Horde_SessionHandler_Driver +{ + /** + * Constructor. + * + * @param array $params Parameters. + */ + public function __construct(array $params = array()) + { + parent::__construct(array_merge($params, array( + 'noset' => true + ))); + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + */ + protected function _open($save_path = null, $session_name = null) + { + } + + /** + * Close the backend. + * + * @throws Horde_Exception + */ + protected function _close() + { + } + + /** + * Read the data for a particular session identifier from the backend. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + protected function _read($id) + { + $file = session_save_path() . DIRECTORY_SEPARATOR . 'sess_' . $id; + $session_data = @file_get_contents($file); + if (($session_data === false) && $this->_logger) { + $this->_logger->log('Unable to read file: ' . $file, 'ERR'); + } + + return strval($session_data); + } + + /** + * Write session data to the backend. + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + protected function _write($id, $session_data) + { + return false; + } + + /** + * Destroy the data for a particular session identifier in the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * + * @return boolean True on success, false otherwise. + */ + public function destroy($id) + { + return false; + } + + /** + * Garbage collect stale sessions from the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param integer $maxlifetime The maximum age of a session. + * + * @return boolean True on success, false otherwise. + */ + public function gc($maxlifetime = 300) + { + return false; + } + + /** + * Get a list of the valid session identifiers. + * + * @return array A list of valid session identifiers. + */ + public function getSessionIDs() + { + $sessions = array(); + + $path = session_save_path(); + + try { + $di = new DirectoryIterator(empty($path) ? Horde_Util::getTempDir() : $path); + } catch (UnexpectedValueException $e) { + return $sessions; + } + + foreach ($di as $key => $val) { + /* Make sure we're dealing with files that start with sess_. */ + if ($val->isFile() && (strpos($key, 'sess_') === 0)) { + $sessions[] = substr($entry, strlen('sess_')); + } + } + + return $sessions; + } + +} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Driver.php b/framework/SessionHandler/lib/Horde/SessionHandler/Driver.php new file mode 100644 index 000000000..fa78adc0b --- /dev/null +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Driver.php @@ -0,0 +1,321 @@ + + * @category Horde + * @package SessionHandler + */ +abstract class Horde_SessionHandler_Driver +{ + /** + * Hash containing connection parameters. + * + * @var array + */ + protected $_params = array(); + + /** + * Initial session data signature. + * + * @var string + */ + protected $_sig; + + /** + * Force saving the session data? + * + * @var boolean + */ + protected $_force = false; + + /** + * Has a connection been made to the backend? + * + * @var boolean + */ + protected $_connected = false; + + /** + * A logger instance. + * + * @var Horde_Log_Logger + */ + protected $_logger; + + /** + * Session variable name. + * + * @var string + */ + protected $_session = 'horde_sh'; + + /** + * Constructor. + * + * @param array $params Parameters: + *
+     * 'logger' - (Horde_Log_Logger) A logger instance.
+     *            DEFAULT: No logging
+     * 'noset' - (boolean) If true, don't set the save handler.
+     *           DEFAULT: false
+     * 
+ */ + public function __construct(array $params = array()) + { + $params = array_merge($this->_params, $params); + + if (isset($params['logger'])) { + $this->_logger = $params['logger']; + unset($params['logger']); + } + + $this->_params = $params; + + register_shutdown_function(array($this, 'shutdown')); + + if (empty($this->_params['noset'])) { + ini_set('session.save_handler', 'user'); + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } + } + + /** + * Destructor. + */ + public function __destruct() + { + /* This is necessary as of PHP 5.0.5 because objects are not available + * when the write() handler is called at the end of a session + * access. */ + session_write_close(); + } + + /** + * Shutdown function. + * + * Used to determine if we need to write the session to avoid a session + * timeout, even though the session is unchanged. + * Theory: On initial login, set the current time plus half of the max + * lifetime in the session. Then check this timestamp before saving. + * If we exceed, force a write of the session and set a new timestamp. + * Why half the maxlifetime? It guarantees that if we are accessing the + * server via a periodic mechanism (think folder refreshing in IMP) that + * we will catch this refresh. + */ + public function shutdown() + { + $curr_time = time(); + + if (!isset($_SESSION[$this->_session]) || + ($curr_time >= $_SESSION[$this->_session])) { + $_SESSION[$this->_session] = $curr_time + (ini_get('session.gc_maxlifetime') / 2); + $this->_force = true; + } + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + * + * @return boolean True on success, false otherwise. + */ + public function open($save_path = null, $session_name = null) + { + if (!$this->_connected) { + try { + $this->_open($save_path, $session_name); + } catch (Horde_SessionHandler_Exception $e) { + if ($this->_logger) { + $this->_logger->log($e, 'ERR'); + } + return false; + } + + $this->_connected = true; + } + + return true; + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + * + * @throws Horde_SessionHandler_Exception + */ + abstract protected function _open($save_path = null, $session_name = null); + + /** + * Close the backend. + * + * @return boolean True on success, false otherwise. + */ + public function close() + { + try { + $this->_close(); + } catch (Horde_SessionHandler_Exception $e) { + if ($this->_logger) { + $this->_logger->log($e, 'ERR'); + } + return false; + } + + $this->_connected = false; + return true; + } + + /** + * Close the backend. + * + * @throws Horde_SessionHandler_Exception + */ + abstract protected function _close(); + + /** + * Read the data for a particular session identifier from the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + public function read($id) + { + $result = $this->_read($id); + $this->_sig = md5($result); + return $result; + } + + /** + * Read the data for a particular session identifier from the backend. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + abstract protected function _read($id); + + /** + * Write session data to the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + public function write($id, $session_data) + { + if (!$this->_force && ($this->_sig == md5($session_data))) { + if ($this->_logger) { + $this->_logger->log('Session data unchanged (id = ' . $id . ')', 'DEBUG'); + } + return true; + } + + return $this->_write($id, $session_data); + } + + /** + * Write session data to the backend. + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + abstract protected function _write($id, $session_data); + + /** + * Destroy the data for a particular session identifier in the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * + * @return boolean True on success, false otherwise. + */ + abstract public function destroy($id); + + /** + * Garbage collect stale sessions from the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param integer $maxlifetime The maximum age of a session. + * + * @return boolean True on success, false otherwise. + */ + abstract public function gc($maxlifetime = 300); + + /** + * Get session data read-only. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + protected function _readOnly($id) + { + return $this->read($id); + } + + /** + * Get a list of the valid session identifiers. + * + * @return array A list of valid session identifiers. + * @throws Horde_SessionHandler_Exception + */ + abstract public function getSessionIDs(); + + /** + * Returns a list of authenticated users and data about their session. + * + * @return array For authenticated users, the sessionid as a key and the + * information returned from Horde_Auth::readSessionData() + * as values. + * @throws Horde_SessionHandler_Exception + */ + public function getSessionsInfo() + { + $sessions = $this->getSessionIDs(); + + $info = array(); + + foreach ($sessions as $id) { + try { + $data = $this->_readOnly($id); + } catch (Horde_SessionHandler_Exception $e) { + continue; + } + $data = Horde_Auth::readSessionData($data, true); + if ($data !== false) { + $info[$id] = $data; + } + } + + return $info; + } + +} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Exception.php b/framework/SessionHandler/lib/Horde/SessionHandler/Exception.php new file mode 100644 index 000000000..b998f8611 --- /dev/null +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Exception.php @@ -0,0 +1,16 @@ + + * @category Horde + * @package SessionHandler + */ +class Horde_SessionHandler_Exception extends Horde_Exception_Prior +{ +} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/External.php b/framework/SessionHandler/lib/Horde/SessionHandler/External.php new file mode 100644 index 000000000..147e4a5cf --- /dev/null +++ b/framework/SessionHandler/lib/Horde/SessionHandler/External.php @@ -0,0 +1,125 @@ + + * @category Horde + * @package SessionHandler + */ +class Horde_SessionHandler_External extends Horde_SessionHandler_Driver +{ + /** + * Constructor. + * + * @param array $params Required parameters: + *
+     * 'close' - (callback) See session_set_save_handler().
+     * 'destroy' - (callback) See session_set_save_handler().
+     * 'gc' - (callback) See session_set_save_handler().
+     * 'open' - (callback) See session_set_save_handler().
+     * 'read' - (callback) See session_set_save_handler().
+     * 'write' - (callback) See session_set_save_handler().
+     * 
+ * + * @throws InvalidArgumentException + */ + public function __construct(array $params = array()) + { + foreach (array('open', 'close', 'read', 'write', 'destroy', 'gc') as $val) { + if (!isset($params[$val])) { + throw new InvalidArgumentException('Missing parameter: ' . $val); + } + } + + parent::__construct($params); + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + */ + protected function _open($save_path = null, $session_name = null) + { + call_user_func($this->_params['open'], $save_path, $session_name); + } + + /** + * Close the backend. + */ + protected function _close() + { + call_user_func($this->_params['close']); + } + + /** + * Read the data for a particular session identifier from the backend. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + protected function _read($id) + { + return call_user_func($this->_params['read']); + } + + /** + * Write session data to the backend. + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + protected function _write($id, $session_data) + { + return call_user_func($this->_params['write'], $id, $session_data); + } + + /** + * Destroy the data for a particular session identifier in the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * + * @return boolean True on success, false otherwise. + */ + public function destroy($id) + { + return call_user_func($this->_params['destroy'], $id); + } + + /** + * Garbage collect stale sessions from the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param integer $maxlifetime The maximum age of a session. + * + * @return boolean True on success, false otherwise. + */ + public function gc($maxlifetime = 300) + { + return call_user_func($this->_params['gc'], $maxlifetime); + } + + /** + * Get a list of the valid session identifiers. + * + * @throws Horde_SessionHandler_Exception + */ + public function getSessionIDs() + { + throw new Horde_SessionHandler_Exception('Driver does not support listing session IDs.'); + } + +} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Ldap.php b/framework/SessionHandler/lib/Horde/SessionHandler/Ldap.php index a3e7767e6..aebee033c 100644 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Ldap.php +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Ldap.php @@ -1,23 +1,19 @@ - * 'hostspec' - (string) The hostname of the ldap server. - * 'port' - (integer) The port number of the ldap server. - * 'dn' - (string) The bind DN. - * 'password' - (string) The bind password. - * + * SessionHandler implementation for LDAP directories. * * This code is adapted from the comments at * http://www.php.net/session-set-save-handler. * + * Copyright 2002-2010 The Horde Project (http://www.horde.org/) + * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * - * @package Horde_SessionHandler + * @category Horde + * @package SessionHandler */ -class Horde_SessionHandler_Ldap extends Horde_SessionHandler +class Horde_SessionHandler_Ldap extends Horde_SessionHandler_Driver { /** * Handle for the current database connection. @@ -27,26 +23,50 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler protected $_conn; /** + * Constructor. + * + * @param array $params Parameters: + *
+     * 'dn' - (string) The bind DN.
+     * 'hostspec' - (string) The hostname of the ldap server.
+     * 'password' - (string) The bind password.
+     * 'port' - (integer) The port number of the ldap server.
+     * 'version' - (integer) [OPTIONAL] TODO
+     * 
+ * + * @throws InvalidArgumentException + */ + public function __construct(array $params = array()) + { + foreach (array('dn', 'hostspec', 'password', 'port') as $val) { + if (!isset($params[$val])) { + throw new InvalidArgumentException('Missing ' . $val . ' parameter.'); + } + } + + parent::__construct($params); + } + + /** * Open the backend. * * @param string $save_path The path to the session object. * @param string $session_name The name of the session. * - * @throws Horde_Exception + * @throws Horde_SessionHandler_Exception */ protected function _open($save_path = null, $session_name = null) { $this->_conn = @ldap_connect($this->_params['hostspec'], $this->_params['port']); // Set protocol version if necessary. - if (isset($this->_params['version'])) { - if (!@ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, $this->_params['version'])) { - throw new Horde_Exception(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], ldap_errno($conn), ldap_error($conn))); - } + if (isset($this->_params['version']) && + !@ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, $this->_params['version'])) { + throw new Horde_SessionHandler_Exception(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], ldap_errno($conn), ldap_error($conn))); } if (!@ldap_bind($this->_conn, $this->_params['dn'], $this->_params['password'])) { - throw new Horde_Exception('Could not bind to LDAP server.'); + throw new Horde_SessionHandler_Exception('Could not bind to LDAP server.'); } } @@ -55,8 +75,8 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler */ protected function _close() { - if (!@ldap_close($this->_conn)) { - throw new Horde_Exception('Could not unbind from LDAP server.'); + if (!@ldap_close($this->_conn) && $this->_logger) { + $this->_logger->log('Could not unbind from LDAP server.', 'INFO'); } } @@ -71,7 +91,10 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler { $sr = @ldap_search($this->_conn, $this->_params['dn'], "(cn=$id)"); $info = @ldap_get_entries($this->_conn, $sr); - return ($info['count'] > 0) ? $info[0]['session'][0] : ''; + + return ($info['count'] > 0) + ? $info[0]['session'][0] + : ''; } /** @@ -84,10 +107,13 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler */ protected function _write($id, $session_data) { - $update = array('objectClass' => array('phpsession', 'top'), - 'session' => $session_data); + $update = array( + 'objectClass' => array('phpsession', 'top'), + 'session' => $session_data + ); $dn = "cn=$id," . $this->_params['dn']; @ldap_delete($this->_conn, $dn); + return @ldap_add($this->_conn, $dn, $update); } @@ -101,6 +127,7 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler public function destroy($id) { $dn = "cn=$id," . $this->_params['dn']; + return @ldap_delete($this->_conn, $dn); } @@ -116,8 +143,9 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler $sr = @ldap_search($this->_conn, $this->_params['dn'], '(objectClass=phpsession)', array('+', 'cn')); $info = @ldap_get_entries($this->_conn, $sr); + if ($info['count'] > 0) { - for ($i = 0; $i < $info['count']; $i++) { + for ($i = 0; $i < $info['count']; ++$i) { $id = $info[$i]['cn'][0]; $dn = "cn=$id," . $this->_params['dn']; $ldapstamp = $info[$i]['modifytimestamp'][0]; @@ -136,4 +164,14 @@ class Horde_SessionHandler_Ldap extends Horde_SessionHandler return true; } + /** + * Get a list of the valid session identifiers. + * + * @throws Horde_SessionHandler_Exception + */ + public function getSessionIDs() + { + throw new Horde_SessionHandler_Exception('Driver does not support listing session IDs.'); + } + } diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Memcache.php b/framework/SessionHandler/lib/Horde/SessionHandler/Memcache.php index e92cc11ab..ec31380cd 100644 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Memcache.php +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Memcache.php @@ -1,31 +1,18 @@ - * 'memcache' - (Horde_Memcache) A memcache object. - * - * - * Optional parameters:
- * 'persistent_driver' - (string) If set, uses this backend to store session
- *                       data persistently.
- * 'persistent_params' - (array) If using a persistent backend, the params
- *                       to use for the persistent backend.
- * 'track' - (boolean) Track active sessions?
- * 'track_lifetime' - (integer) The number of seconds after which tracked
- *                    sessions will be treated as expired.
- * 
+ * SessionHandler implementation for memcache. * * Copyright 2005-2010 The Horde Project (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * - * @author Rong-En Fan - * @author Michael Slusarz - * @package Horde_SessionHandler + * @author Rong-En Fan + * @author Michael Slusarz + * @category Horde + * @package SessionHandler */ -class Horde_SessionHandler_Memcache extends Horde_SessionHandler +class Horde_SessionHandler_Memcache extends Horde_SessionHandler_Driver { /** * Memcache object. @@ -42,13 +29,6 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler protected $_id; /** - * Persistent backend driver. - * - * @var Horde_SessionHandler - */ - protected $_persistent; - - /** * Do read-only get? * * @var boolean @@ -65,36 +45,29 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler /** * Constructor. * - * @param array $params A hash containing connection parameters. + * @param array $params Parameters: + *
+     * 'memcache' - (Horde_Memcache) [REQUIRED] A memcache object.
+     * 'track' - (boolean) Track active sessions?
+     * 'track_lifetime' - (integer) The number of seconds after which tracked
+     *                    sessions will be treated as expired.
+     * 
* - * @throws Horde_Exception * @throws InvalidArgumentException */ - protected function __construct($params = array()) + public function __construct(array $params = array()) { if (empty($params['memcache'])) { - throw InvalidArgumentException('Missing memcache object.'); + throw InvalidArgumentException('Missing memcache argument.'); } $this->_memcache = $params['memcache']; - - if (!empty($params['persistent_driver'])) { - try { - $this->_persistent = self::singleton($params['persistent_driver'], empty($params['persistent_params']) ? null : $params['persistent_params']); - } catch (Horde_Exception $e) { - throw new Horde_Exception('Horde is unable to correctly start the persistent session handler.'); - } - } + unset($params['memcache']); parent::__construct($params); - // If using a persistent backend, don't track sessions in memcache - if (isset($this->_persistent)) { - $this->_params['track'] = false; - } - - if (empty($this->_params['track_lifetime'])) { - $this->_params['track_lifetime'] = ini_get('session.gc_maxlifetime'); + if (empty($this->_params['track_lt'])) { + $this->_params['track_lt'] = ini_get('session.gc_maxlifetime'); } if (!empty($this->_params['track']) && (rand(0, 999) == 0)) { @@ -108,30 +81,20 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler * @param string $save_path The path to the session object. * @param string $session_name The name of the session. * - * @throws Horde_Exception + * @throws Horde_SessionHandler_Exception */ protected function _open($save_path = null, $session_name = null) { - if (isset($this->_persistent)) { - if (!$this->_persistent->open($save_path, $session_name)) { - throw new Horde_Exception('Could not open persistent backend.'); - } - } } /** * Close the backend. - * - * @throws Horde_Exception */ protected function _close() { if (isset($this->_id)) { $this->_memcache->unlock($this->_id); } - if (isset($this->_persistent)) { - $this->_persistent->close(); - } } /** @@ -153,23 +116,22 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler $this->_memcache->unlock($id); } - if (isset($this->_persistent)) { - $result = $this->_persistent->read($id); - } - if ($result === false) { - Horde::logMessage('Error retrieving session data (id = ' . $id . ')', 'DEBUG'); + if ($this->_logger) { + $this->_logger->log('Error retrieving session data (id = ' . $id . ')', 'DEBUG'); + } return false; } - - $this->_persistent->write($id, $session_data); } if (!$this->_readonly) { $this->_id = $id; } - Horde::logMessage('Read session data (id = ' . $id . ')', 'DEBUG'); + if ($this->_logger) { + $this->_logger->log('Read session data (id = ' . $id . ')', 'DEBUG'); + } + return $result; } @@ -195,14 +157,12 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler if (!$res && !$this->_memcache->set($id, $session_data)) { - Horde::logMessage('Error writing session data (id = ' . $id . ')', 'ERR'); + if ($this->_logger) { + $this->_logger->log('Error writing session data (id = ' . $id . ')', 'ERR'); + } return false; } - if (isset($this->_persistent)) { - $result = $this->_persistent->write($id, $session_data); - } - if ($track) { $this->_memcache->lock($this->_trackID); $ids = $this->_memcache->get($this->_trackID); @@ -215,7 +175,10 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler $this->_memcache->unlock($this->_trackID); } - Horde::logMessage('Wrote session data (id = ' . $id . ')', 'DEBUG'); + if ($this->_logger) { + $this->_logger->log('Wrote session data (id = ' . $id . ')', 'DEBUG'); + } + return true; } @@ -232,14 +195,12 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler $this->_memcache->unlock($id); if ($result === false) { - Horde::logMessage('Failed to delete session (id = ' . $id . ')', 'DEBUG'); + if ($this->_logger) { + $this->_logger->log('Failed to delete session (id = ' . $id . ')', 'DEBUG'); + } return false; } - if (isset($this->_persistent)) { - $result = $this->_persistent->destroy($id); - } - if (!empty($this->_params['track'])) { $this->_memcache->lock($this->_trackID); $ids = $this->_memcache->get($this->_trackID); @@ -250,7 +211,10 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler $this->_memcache->unlock($this->_trackID); } - Horde::logMessage('Deleted session data (id = ' . $id . ')', 'DEBUG'); + if ($this->_logger) { + $this->_logger->log('Deleted session data (id = ' . $id . ')', 'DEBUG'); + } + return true; } @@ -263,45 +227,29 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler */ public function gc($maxlifetime = 300) { - $result = true; - - if (isset($this->_persistent)) { - $result = $this->_persistent->gc($maxlifetime); - } - // Memcache does its own garbage collection. - return $result; + return true; } /** * Get a list of (possibly) valid session identifiers. * * @return array A list of session identifiers. - * @throws Horde_Exception + * @throws Horde_SessionHandler_Exception */ public function getSessionIDs() { - if (isset($this->_persistent)) { - return $this->_persistent->getSessionIDs(); - } - - try { - $this->_open(); - - if (empty($this->_params['track'])) { - throw new Horde_Exception(_("Memcache session tracking not enabled.")); - } - } catch (Horde_Exception $e) { - if (isset($this->_persistent)) { - return $this->_persistent->getSessionIDs(); - } - throw $e; + if (empty($this->_params['track'])) { + throw new Horde_SessionHandler_Exception('Memcache session tracking not enabled.'); } $this->trackGC(); $ids = $this->_memcache->get($this->_trackID); - return ($ids === false) ? array() : array_keys($ids); + + return ($ids === false) + ? array() + : array_keys($ids); } /** @@ -316,6 +264,7 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler $this->_readonly = true; $result = $this->_memcache->get($id); $this->_readonly = false; + return $result; } @@ -331,7 +280,7 @@ class Horde_SessionHandler_Memcache extends Horde_SessionHandler return; } - $tstamp = time() - $this->_params['track_lifetime']; + $tstamp = time() - $this->_params['track_lt']; $alter = false; foreach ($ids as $key => $val) { diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Mysql.php b/framework/SessionHandler/lib/Horde/SessionHandler/Mysql.php deleted file mode 100644 index 901e62dd7..000000000 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Mysql.php +++ /dev/null @@ -1,305 +0,0 @@ - - * 'hostspec' - (string) The hostname of the database server. - * 'protocol' - (string) The communication protocol ('tcp', 'unix', etc.). - * 'username' - (string) The username with which to connect to the - * database. - * 'password' - (string) The password associated with 'username'. - * 'database' - (string) The name of the database. - * 'table' - (string) The name of the sessiondata table in 'database'. - * 'rowlocking' - (boolean) Whether to use row-level locking and - * transactions (InnoDB) or table-level locking (MyISAM). - * - * - * Required for some configurations:
- *   'port' - (integer) The port on which to connect to the database.
- * 
- * - * Optional parameters:
- *   'persistent' - (boolean) Use persistent DB connections?
- * 
- * - * The table structure can be found in: - * horde/scripts/sql/horde_sessionhandler.sql. - * - * Copyright 2002-2010 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. - * - * @author Mike Cochrame - * @author Chuck Hagenbuch - * @author Jan Schneider - * @package Horde_SessionHandler - */ -class Horde_SessionHandler_Mysql extends Horde_SessionHandler -{ - /** - * Handle for the current database connection. - * - * @var resource - */ - protected $_db; - - /** - * Attempts to open a connection to the SQL server. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @throws Horde_Exception - */ - protected function _open($save_path = null, $session_name = null) - { - Horde::assertDriverConfig($this->_params, 'sessionhandler', - array('hostspec', 'username', 'database'), - 'session handler MySQL'); - - if (empty($this->_params['password'])) { - $this->_params['password'] = ''; - } - - if (empty($this->_params['table'])) { - $this->_params['table'] = 'horde_sessionhandler'; - } - - $connect = empty($this->_params['persistent']) - ? 'mysql_connect' - : 'mysql_pconnect'; - - if (!$this->_db = @$connect($this->_params['hostspec'] . (!empty($this->_params['port']) ? ':' . $this->_params['port'] : ''), - $this->_params['username'], - $this->_params['password'])) { - throw new Horde_Exception('Could not connect to database for SQL Horde_SessionHandler.'); - } - - if (!@mysql_select_db($this->_params['database'], $this->_db)) { - throw new Horde_Exception(sprintf('Could not connect to database %s for SQL Horde_SessionHandler.', $this->_params['database'])); - } - } - - /** - * Close the backend. - * - * @throws Horde_Exception - */ - protected function _close() - { - /* Disconnect from database. */ - if (!@mysql_close($this->_db)) { - throw new Horde_Exception('Could not disconnect from database.'); - } - } - - /** - * Read the data for a particular session identifier from the backend. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _read($id) - { - /* Select db */ - if (!@mysql_select_db($this->_params['database'], $this->_db)) { - return ''; - } - - $query = sprintf('SELECT session_data FROM %s WHERE session_id = %s', - $this->_params['table'], - $this->_quote($id)); - - if (!empty($this->_params['rowlocking'])) { - /* Start a transaction. */ - $result = @mysql_query('START TRANSACTION', $this->_db); - $query .= ' FOR UPDATE'; - } else { - $result = @mysql_query('LOCK TABLES ' . $this->_params['table'] . ' WRITE', $this->_db); - } - if (!$result) { - return ''; - } - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Mysql::_read(): query = "%s"', $query), 'DEBUG'); - - $result = @mysql_query($query, $this->_db); - if (!$result) { - Horde::logMessage('Error retrieving session data (id = ' . $id . '): ' . mysql_error($this->_db), 'ERR'); - return ''; - } - - return @mysql_result($result, 0, 0); - } - - /** - * Write session data to the backend. - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - protected function _write($id, $session_data) - { - /* Select db */ - if (!@mysql_select_db($this->_params['database'], $this->_db)) { - return ''; - } - - /* Build the SQL query. */ - $query = sprintf('REPLACE INTO %s (session_id, session_data, session_lastmodified)' . - ' VALUES (%s, %s, %s)', - $this->_params['table'], - $this->_quote($id), - $this->_quote($session_data), - time()); - - $result = @mysql_query($query, $this->_db); - if (!$result) { - $error = mysql_error($this->_db); - } - if (empty($this->_params['rowlocking'])) { - @mysql_query('UNLOCK TABLES ' . $this->_params['table'], $this->_db); - } - if (!$result) { - @mysql_query('ROLLBACK', $this->_db); - Horde::logMessage('Error writing session data: ' . $error, 'ERR'); - return false; - } - - @mysql_query('COMMIT', $this->_db); - - return true; - } - - /** - * Destroy the data for a particular session identifier in the backend. - * - * @param string $id The session identifier. - * - * @return boolean True on success, false otherwise. - */ - public function destroy($id) - { - /* Select db */ - if (!@mysql_select_db($this->_params['database'], $this->_db)) { - return ''; - } - - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_id = %s', - $this->_params['table'], $this->_quote($id)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Mysql::destroy(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $result = @mysql_query($query, $this->_db); - if (!$result) { - $error = mysql_error($this->_db); - } - if (empty($this->_params['rowlocking'])) { - @mysql_query('UNLOCK TABLES ' . $this->_params['table'], $this->_db); - } - if (!$result) { - @mysql_query('ROLLBACK', $this->_db); - Horde::logMessage('Failed to delete session (id = ' . $id . '): ' . $error, 'ERR'); - return false; - } - - @mysql_query('COMMIT', $this->_db); - - return true; - } - - /** - * Garbage collect stale sessions from the backend. - * - * @param integer $maxlifetime The maximum age of a session. - * - * @return boolean True on success, false otherwise. - */ - public function gc($maxlifetime = 300) - { - /* Select db */ - if (!@mysql_select_db($this->_params['database'], $this->_db)) { - return ''; - } - - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_lastmodified < %s', - $this->_params['table'], (int)(time() - $maxlifetime)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Mysql::gc(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $result = @mysql_query($query, $this->_db); - if (!$result) { - Horde::logMessage('Error garbage collecting old sessions: ' . mysql_error($this->_db), 'ERR'); - return false; - } - - return @mysql_affected_rows($this->_db); - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - * @throws Horde_Exception - */ - public function getSessionIDs() - { - /* Make sure we have a valid database connection. */ - $this->open(); - - $query = sprintf('SELECT session_id FROM %s' . - ' WHERE session_lastmodified >= %s', - $this->_params['table'], - time() - ini_get('session.gc_maxlifetime')); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Mysql::getSessionIDs(): query = "%s"', $query), 'DEBUG'); - - $result = @mysql_query($query, $this->_db); - if (!$result) { - throw new Horde_Exception('Error getting session IDs: ' . mysql_error($this->_db)); - } - - $sessions = array(); - - while ($row = mysql_fetch_row($result)) { - $sessions[] = $row[0]; - } - - return $sessions; - } - - /** - * Escape a mysql string. - * - * @param string $value The string to quote. - * - * @return string The quoted string. - */ - protected function _quote($value) - { - switch (strtolower(gettype($value))) { - case 'null': - return 'NULL'; - - case 'integer': - return $value; - - case 'string': - default: - return "'" . @mysql_real_escape_string($value, $this->_db) . "'"; - } - } - -} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/None.php b/framework/SessionHandler/lib/Horde/SessionHandler/None.php deleted file mode 100644 index 1a6be3c74..000000000 --- a/framework/SessionHandler/lib/Horde/SessionHandler/None.php +++ /dev/null @@ -1,67 +0,0 @@ - - * None. - * - * Optional parameters:
- *   None.
- * - * Copyright 2005-2010 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. - * - * @author Matt Selsky - * @package Horde_SessionHandler - */ -class Horde_SessionHandler_None extends Horde_SessionHandler -{ - /** - * Read the data for a particular session identifier from the backend. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _read($id) - { - $file = session_save_path() . DIRECTORY_SEPARATOR . 'sess_' . $id; - $session_data = @file_get_contents($file); - if ($session_data === false) { - Horde::logMessage('Unable to read file: ' . $file, 'ERR'); - $session_data = ''; - } - - return $session_data; - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - */ - public function getSessionIDs() - { - $sessions = array(); - - $path = session_save_path(); - $d = @dir(empty($path) ? Horde_Util::getTempDir() : $path); - if (!$d) { - return $sessions; - } - - while (($entry = $d->read()) !== false) { - /* Make sure we're dealing with files that start with - * sess_. */ - if (is_file($d->path . DIRECTORY_SEPARATOR . $entry) && - !strncmp($entry, 'sess_', strlen('sess_'))) { - $sessions[] = substr($entry, strlen('sess_')); - } - } - - return $sessions; - } - -} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Oci8.php b/framework/SessionHandler/lib/Horde/SessionHandler/Oci8.php deleted file mode 100644 index 4939dd5e9..000000000 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Oci8.php +++ /dev/null @@ -1,278 +0,0 @@ - - * 'hostspec' - (string) The hostname of the database server. - * 'username' - (string) The username with which to connect to the database. - * 'password' - (string) The password associated with 'username'. - * 'database' - (string) The name of the database. - * 'table' - (string) The name of the sessiondata table in 'database'. - * - * - * Required for some configurations:
- *   'port' - (integer) The port on which to connect to the database.
- * 
- * - * Optional parameters:
- *   'persistent' - (boolean) Use persistent DB connections?
- * 
- - * The table structure can be found in: - * horde/scripts/sql/horde_sessionhandler.oci8.sql. - * - * Copyright 2003-2009 Liam Hoekenga - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. - * - * @author Liam Hoekenga - * @package Horde_SessionHandler - */ -class Horde_SessionHandler_Oci8 extends Horde_SessionHandler -{ - /** - * Handle for the current database connection. - * - * @var resource - */ - protected $_db; - - /** - * Attempts to open a connection to the SQL server. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @throws Horde_Exception - */ - protected function _open($save_path = false, $session_name = false) - { - Horde::assertDriverConfig($this->_params, 'sessionhandler', - array('hostspec', 'username', 'password'), - 'session handler Oracle'); - - if (!isset($this->_params['table'])) { - $this->_params['table'] = 'horde_sessionhandler'; - } - - if (function_exists('oci_connect')) { - $connect = empty($this->_params['persistent']) - ? 'oci_connect' - : 'oci_pconnect'; - } else { - $connect = empty($this->_params['persistent']) - ? 'OCILogon' - : 'OCIPLogon'; - } - - if (!is_resource($this->_db = @$connect($this->_params['username'], - $this->_params['password'], - $this->_params['hostspec']))) { - throw new Horde_Exception('Could not connect to database for SQL Horde_SessionHandler.'); - } - } - - /** - * Close the backend. - * - * @throws Horde_Exception - */ - protected function _close() - { - if (!OCILogOff($this->_db)) { - throw new Horde_Exception('Could not disconnect from databse.'); - } - } - - /** - * Read the data for a particular session identifier from the backend. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _read($id) - { - $select_query = sprintf('SELECT session_data FROM %s WHERE session_id = %s FOR UPDATE', - $this->_params['table'], $this->_quote($id)); - - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::_read(): query = "%s"', $select_query), 'DEBUG'); - - $select_statement = OCIParse($this->_db, $select_query); - OCIExecute($select_statement, OCI_DEFAULT); - if (OCIFetchInto($select_statement, $result)) { - $value = $result[0]->load(); - } else { - $value = ''; - } - - OCIFreeStatement($select_statement); - - return $value; - } - - /** - * Write session data to the backend. - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - protected function _write($id, $session_data) - { - $select_query = sprintf('SELECT session_data FROM %s WHERE session_id = %s FOR UPDATE', - $this->_params['table'], $this->_quote($id)); - - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::_write(): query = "%s"', $select_query), 'DEBUG'); - - $select_statement = OCIParse($this->_db, $select_query); - OCIExecute($select_statement, OCI_DEFAULT); - if (OCIFetchInto($select_statement, $result)) { - /* Discard the existing LOB contents. */ - if (!$result[0]->truncate()) { - OCIRollback($this->_db); - return false; - } - - /* Save the session data. */ - if ($result[0]->save($session_data)) { - OCICommit($this->_db); - OCIFreeStatement($select_statement); - } else { - OCIRollback($this->_db); - return false; - } - } else { - $insert_query = sprintf('INSERT INTO %s (session_id, session_lastmodified, session_data) VALUES (%s, %s, EMPTY_BLOB()) RETURNING session_data INTO :blob', - $this->_params['table'], - $this->_quote($id), - $this->_quote(time())); - - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::_read(): query = "%s"', $insert_query), 'DEBUG'); - - $insert_statement = OCIParse($this->_db, $insert_query); - $lob = OCINewDescriptor($this->_db); - OCIBindByName($insert_statement, ':blob', $lob, -1, SQLT_BLOB); - OCIExecute($insert_statement, OCI_DEFAULT); - if (!$lob->save($session_data)) { - OCIRollback($this->_db); - return false; - } - OCICommit($this->_db); - OCIFreeStatement($insert_statement); - } - - return true; - } - - /** - * Destroy the data for a particular session identifier in the backend. - * - * @param string $id The session identifier. - * - * @return boolean True on success, false otherwise. - */ - public function destroy($id) - { - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_id = %s', - $this->_params['table'], $this->_quote($id)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::destroy(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $statement = OCIParse($this->_db, $query); - $result = OCIExecute($statement); - if (!$result) { - OCIFreeStatement($statement); - Horde::logMessage('Failed to delete session (id = ' . $id . ')', 'ERR'); - return false; - } - - OCIFreeStatement($statement); - - return true; - } - - /** - * Garbage collect stale sessions from the backend. - * - * @param integer $maxlifetime The maximum age of a session. - * - * @return boolean True on success, false otherwise. - */ - public function gc($maxlifetime = 1) - { - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_lastmodified < %s', - $this->_params['table'], $this->_quote(time() - $maxlifetime)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::gc(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $statement = OCIParse($this->_db, $query); - $result = OCIExecute($statement); - if (!$result) { - OCIFreeStatement($statement); - Horde::logMessage('Error garbage collecting old sessions', 'ERR'); - return false; - } - - OCIFreeStatement($statement); - - return true; - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - * @throws Horde_Exception - */ - public function getSessionIDs() - { - /* Make sure we have a valid database connection. */ - $this->open(); - - /* Session timeout, don't rely on garbage collection */ - $query = sprintf('SELECT session_id FROM %s ' . - 'WHERE session_lastmodified >= %s', - $this->_params['table'], - time() - ini_get('session.gc_maxlifetime')); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Oci8::getSessionIDs(): query = "%s"', $query), 'DEBUG'); - - /* Execute query */ - $statement = OCIParse($this->_db, $query); - OCIExecute($statement); - - $sessions = array(); - while (OCIFetchInto($statement, $row)) { - $sessions[] = $row[0]; - } - - OCIFreeStatement($statement); - - return $sessions; - } - - /** - * Escape a string for insertion. Stolen from PEAR::DB. - * - * @param string $value The string to quote. - * - * @return string The quoted string. - */ - protected function _quote($value) - { - return is_null($value) - ? 'NULL' - : "'" . str_replace("'", "''", $value) . "'"; - } - -} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Pgsql.php b/framework/SessionHandler/lib/Horde/SessionHandler/Pgsql.php deleted file mode 100644 index ad164b740..000000000 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Pgsql.php +++ /dev/null @@ -1,285 +0,0 @@ - - * 'database' - (string) The name of the database. - * 'password' - (string) The password associated with 'username'. - * 'protocol' - (string) The communication protocol ('tcp', 'unix'). - * 'username' - (string) The username with which to connect to the database. - * - * Required for some configurations (i.e. 'protocol' = 'tcp'):
- *   'hostspec' - (string) The hostname of the database server.
- *   'port'     - (integer) The port on which to connect to the database.
- * 
- * - * Optional parameters:
- *   'persistent' - (boolean) Use persistent DB connections?
- *                  Default: NO
- *   'table'      - (string) The name of the sessiondata table in 'database'.
- *                  Default: 'horde_sessionhandler'
- * - - * The table structure can be found in: - * horde/scripts/sql/horde_sessionhandler.pgsql.sql. - * - * Contributors:
- *  Jason Carlson           Return an empty string on failed reads
- *  pat@pcprogrammer.com    Perform update in a single transaction
- *  Jonathan Crompton       Lock row for life of session
- * - * @author Jon Parise - * @package Horde_SessionHandler - */ -class Horde_SessionHandler_Pgsql extends Horde_SessionHandler -{ - /** - * Handle for the current database connection. - * - * @var resource - */ - protected $_db; - - /** - * Attempts to open a connection to the SQL server. - * - * @param string $save_path The path to the session object. - * @param string $session_name The name of the session. - * - * @throws Horde_Exception - */ - protected function _open($save_path = null, $session_name = null) - { - Horde::assertDriverConfig($this->_params, 'sessionhandler', - array('hostspec', 'username', 'database', 'password'), - 'session handler pgsql'); - - if (empty($this->_params['table'])) { - $this->_params['table'] = 'horde_sessionhandler'; - } - - $connect = empty($this->_params['persistent']) ? - 'pg_connect' :'pg_pconnect'; - - $paramstr = ''; - if (isset($this->_params['protocol']) && - $this->_params['protocol'] == 'tcp') { - $paramstr .= ' host=' . $this->_params['hostspec']; - if (isset($this->_params['port'])) { - $paramstr .= ' port=' . $this->_params['port']; - } - } - $paramstr .= ' dbname=' . $this->_params['database'] . - ' user=' . $this->_params['username'] . - ' password=' . $this->_params['password']; - - if (!$this->_db = @$connect($paramstr)) { - throw new Horde_Exception(sprintf('Could not connect to database %s for SQL Horde_SessionHandler.', $this->_params['database'])); - } - } - - /** - * Close the backend. - * - * @throws Horde_Exception - */ - protected function _close() - { - /* Disconnect from database. */ - if (!@pg_close($this->_db)) { - throw new Horde_Exception('Cound not disconnect from database.'); - } - } - - /** - * Read the data for a particular session identifier from the backend. - * - * @param string $id The session identifier. - * - * @return string The session data. - */ - protected function _read($id) - { - @pg_query($this->_db, 'BEGIN;'); - - $query = sprintf('SELECT session_data FROM %s WHERE session_id = %s ' . - 'FOR UPDATE;', - $this->_params['table'], - $this->_quote($id)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Pgsql::' . '_read(): query = "%s"', $query), 'DEBUG'); - - $result = @pg_query($this->_db, $query); - $data = pg_fetch_result($result, 0, 'session_data'); - pg_free_result($result); - - return pack('H*', $data); - } - - /** - * Write session data to the backend. - * - * @param string $id The session identifier. - * @param string $session_data The session data. - * - * @return boolean True on success, false otherwise. - */ - protected function _write($id, $session_data) - { - $query = sprintf('SELECT session_data FROM %s WHERE session_id = %s ' . - 'FOR UPDATE', - $this->_params['table'], - $this->_quote($id)); - $result = @pg_query($this->_db, $query); - $rows = pg_num_rows($result); - pg_free_result($result); - - if ($rows == 0) { - $query = sprintf('INSERT INTO %s (session_id, ' . - 'session_lastmodified, session_data) ' . - 'VALUES (%s, %s, %s);', - $this->_params['table'], - $this->_quote($id), - time(), - $this->_quote(bin2hex($session_data))); - } else { - $query = sprintf('UPDATE %s SET session_lastmodified = %s, ' . - 'session_data = %s WHERE session_id = %s;', - $this->_params['table'], - time(), - $this->_quote(bin2hex($session_data)), - $this->_quote($id)); - } - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Pgsql::' . '_write(): query = "%s"', $query), 'DEBUG'); - - $result = @pg_query($this->_db, $query); - $rows = pg_affected_rows($result); - pg_free_result($result); - - @pg_query($this->_db, 'COMMIT;'); - - if ($rows != 1) { - Horde::logMessage('Error writing session data', 'ERR'); - return false; - } - - return true; - } - - /** - * Destroy the data for a particular session identifier in the backend. - * - * @param string $id The session identifier. - * - * @return boolean True on success, false otherwise. - */ - public function destroy($id) - { - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_id = %s;', - $this->_params['table'], $this->_quote($id)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Pgsql::' . 'destroy(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $result = @pg_query($this->_db, $query); - - @pg_query($this->_db, 'COMMIT;'); - - if (!$result) { - pg_free_result($result); - Horde::logMessage('Failed to delete session (id = ' . $id . ')', 'ERR'); - return false; - } - - pg_free_result($result); - return true; - } - - /** - * Garbage collect stale sessions from the backend. - * - * @param integer $maxlifetime The maximum age of a session. - * - * @return boolean True on success, false otherwise. - */ - public function gc($maxlifetime = 300) - { - /* Build the SQL query. */ - $query = sprintf('DELETE FROM %s WHERE session_lastmodified < %s', - $this->_params['table'], - $this->_quote(time() - $maxlifetime)); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Pgsql::' . 'gc(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $result = @pg_query($this->_db, $query); - if (!$result) { - Horde::logMessage('Error garbage collecting old sessions', 'ERR'); - } - - pg_free_result($result); - - return $result; - } - - /** - * Get a list of the valid session identifiers. - * - * @return array A list of valid session identifiers. - * @throws Horde_Exception - */ - public function getSessionIDs() - { - /* Make sure we have a valid database connection. */ - $this->open(); - - /* Build the SQL query. */ - $query = sprintf('SELECT session_id FROM %s ' . - 'WHERE session_lastmodified >= %s', - $this->_params['table'], - time() - ini_get('session.gc_maxlifetime')); - - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Pgsql::' . 'getSessionIDs(): query = "%s"', $query), 'DEBUG'); - - /* Execute the query. */ - $result = @pg_query($this->_db, $query); - if (!$result) { - pg_free_result($result); - throw new Horde_Exception('Error getting session IDs'); - } - - $sessions = array(); - while ($row = pg_fetch_row($result)) { - $sessions[] = $row[0]; - } - - pg_free_result($result); - - return $sessions; - } - - /** - * Escape a string for insertion into the database. - * - * @param string $value The string to quote. - * - * @return string The quoted string. - */ - protected function _quote($value) - { - return "'" . addslashes($value) . "'"; - } - -} diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Sql.php b/framework/SessionHandler/lib/Horde/SessionHandler/Sql.php index 0be3fa79e..0bbbad808 100644 --- a/framework/SessionHandler/lib/Horde/SessionHandler/Sql.php +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Sql.php @@ -1,7 +1,6 @@ - * @package Horde_SessionHandler + * @author Mike Cochrane + * @category Horde + * @package SessionHandler */ -class Horde_SessionHandler_Sql extends Horde_SessionHandler +class Horde_SessionHandler_Sql extends Horde_SessionHandler_Driver { /** * Handle for the current database connection. * - * @var DB + * @var Horde_Db_Adapter_Base */ protected $_db; /** - * Handle for the current database connection, used for writing. Defaults - * to the same handle as $_db if a separate write database is not required. - * - * @var DB - */ - protected $_write_db; - - /** * Constructor. * * @param array $params Parameters: *
-     * 'db' - (DB) [REQUIRED] The DB instance.
-     * 'persistent' - (boolean) Use persistent DB connections?
-     *                DEFAULT: false
-     * 'table' - (string) The name of the tokens table in 'database'.
-     *           DEFAULT: 'horde_tokens'
-     * 'write_db' - (DB) The write DB instance.
+     * 'db' - (Horde_Db_Adapter_Base) [REQUIRED] The DB instance.
+     * 'table' - (string) The name of the sessions table.
+     *           DEFAULT: 'horde_sessionhandler'
      * 
* - * @throws Horde_Exception + * @throws InvalidArgumentException */ - public function __construct($params = array()) + public function __construct(array $params = array()) { if (!isset($params['db'])) { - throw new Horde_Exception('Missing db parameter.'); + throw new InvalidArgumentException('Missing db parameter.'); } $this->_db = $params['db']; + unset($params['db']); - $this->_write_db = isset($params['write_db']) - ? $params['write_db'] - : $this->_db; - - if (isset($params['write_db'])) { - $this->_write_db = $params['write_db']; - } - - unset($params['db'], $params['write_db']); - - $params = array_merge(array( - 'persistent' => false, + parent::__construct(array_merge(array( 'table' => 'horde_sessionhandler' - ), $params); - - parent::__construct($params); + ), $params)); } /** * Close the backend. * - * @throws Horde_Exception + * @throws Horde_SessionHandler_Exception */ protected function _close() { /* Close any open transactions. */ - $this->_db->commit(); - $this->_db->autoCommit(true); - @$this->_db->disconnect(); - - if ($this->_db != $this->_write_db) { - $this->_write_db->commit(); - $this->_write_db->autoCommit(true); - @$this->_write_db->disconnect(); + try { + $this->_db->commitDbTransaction(); + } catch (Horde_Db_Exception $e) { + throw new Horde_SessionHandler_Exception($e); } } @@ -100,21 +73,20 @@ class Horde_SessionHandler_Sql extends Horde_SessionHandler protected function _read($id) { /* Begin a transaction. */ - $result = $this->_write_db->autocommit(false); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); - return ''; - } + // TODO: Rowlocking in Mysql + $this->_db->beginDbTransaction(); - /* Execute the query. */ - $result = Horde_SQL::readBlob($this->_write_db, $this->_params['table'], 'session_data', array('session_id' => $id)); + /* Build query. */ + $query = sprintf('SELECT session_data FROM %s WHERE session_id = ?', + $this->_params['table']); + $values = array($id); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); - return ''; + /* Execute the query. */ + try { + return $this->_db->selectValue($query, $values); + } catch (Horde_Db_Exception $e) { + return false; } - - return $result; } /** @@ -132,41 +104,33 @@ class Horde_SessionHandler_Sql extends Horde_SessionHandler $this->_params['table']); $values = array($id); - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Sql::write(): query = "%s"', $query), 'DEBUG'); - /* Execute the query. */ - $result = $this->_write_db->getOne($query, $values); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); - return false; - } - - if ($result) { - $result = Horde_SQL::updateBlob($this->_write_db, $this->_params['table'], 'session_data', - $session_data, array('session_id' => $id), - array('session_lastmodified' => time())); - } else { - $result = Horde_SQL::insertBlob($this->_write_db, $this->_params['table'], 'session_data', - $session_data, array('session_id' => $id, - 'session_lastmodified' => time())); - } - - if (is_a($result, 'PEAR_Error')) { - $this->_write_db->rollback(); - $this->_write_db->autoCommit(true); - Horde::logMessage($result, 'ERR'); + try { + $result = $this->_db->selectValue($query, $values); + } catch (Horde_Db_Exception $e) { return false; } - $result = $this->_write_db->commit(); - if (is_a($result, 'PEAR_Error')) { - $this->_write_db->autoCommit(true); - Horde::logMessage($result, 'ERR'); + /* Build the replace SQL query. */ + $query = sprintf('REPLACE INTO %s ' . + '(session_id, session_data, session_lastmodified) ' . + 'VALUES (?, ?, ?)', + $this->_params['table']); + $values = array( + $id, + $session_data, + time() + ); + + /* Execute the replace query. */ + try { + $this->_db->update($query, $values); + $this->_db->commitDbTransaction(); + } catch (Horde_Db_Exception $e) { + $this->_db->rollbackDbTransaction(); return false; } - $this->_write_db->autoCommit(true); return true; } @@ -184,19 +148,11 @@ class Horde_SessionHandler_Sql extends Horde_SessionHandler $this->_params['table']); $values = array($id); - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Sql::destroy(): query = "%s"', $query), 'DEBUG'); - /* Execute the query. */ - $result = $this->_write_db->query($query, $values); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); - return false; - } - - $result = $this->_write_db->commit(); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); + try { + $this->_db->delete($query, $values); + $this->_db->commitDbTransaction(); + } catch (Horde_Db_Exception $e) { return false; } @@ -217,13 +173,10 @@ class Horde_SessionHandler_Sql extends Horde_SessionHandler $this->_params['table']); $values = array(time() - $maxlifetime); - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Sql::gc(): query = "%s"', $query), 'DEBUG'); - /* Execute the query. */ - $result = $this->_write_db->query($query, $values); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); + try { + $this->_db->delete($query, $values); + } catch (Horde_Db_Exception $e) { return false; } @@ -234,28 +187,23 @@ class Horde_SessionHandler_Sql extends Horde_SessionHandler * Get a list of the valid session identifiers. * * @return array A list of valid session identifiers. - * @throws Horde_Exception */ public function getSessionIDs() { $this->open(); /* Build the SQL query. */ - $query = 'SELECT session_id FROM ' . $this->_params['table'] . - ' WHERE session_lastmodified >= ?'; + $query = sprintf('SELECT session_id FROM %s' . + ' WHERE session_lastmodified >= ?', + $this->_params['table']); $values = array(time() - ini_get('session.gc_maxlifetime')); - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('SQL Query by Horde_SessionHandler_Sql::getSessionIDs(): query = "%s"', $query), 'DEBUG'); - /* Execute the query. */ - $result = $this->_db->getCol($query, 0, $values); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage($result, 'ERR'); - return false; + try { + return $this->_db->selectValues($query, $values); + } catch (Horde_Db_Exception $e) { + return array(); } - - return $result; } } diff --git a/framework/SessionHandler/lib/Horde/SessionHandler/Stack.php b/framework/SessionHandler/lib/Horde/SessionHandler/Stack.php new file mode 100644 index 000000000..c06e9441e --- /dev/null +++ b/framework/SessionHandler/lib/Horde/SessionHandler/Stack.php @@ -0,0 +1,184 @@ + + * @category Horde + * @package SessionHandler + */ +class Horde_SessionHandler_Stack extends Horde_SessionHandler_Driver +{ + /** + * Stack of sessionhandlers. + * + * @var string + */ + protected $_stack = array(); + + /** + * Constructor. + * + * @param array $params Parameters: + *
+     * 'stack' - (array) [REQUIRED] A list of sessionhandlers to loop
+     *           through, in order of priority. The last entry is considered
+     *           the "master" server.
+     *           Each value should contain an array with two keys: 'driver', a
+     *           string value with the SessionHandler driver to use, and
+     *           'params', containing any parameters needed by this driver.
+     * 
+ * + * @throws InvalidArgumentException + */ + public function __construct(array $params = array()) + { + if (!isset($params['stack'])) { + throw new InvalidArgumentException('Missing stack parameter.'); + } + + foreach ($params['stack'] as $val) { + $this->_stack[] = Horde_SessionHandler::factory($val['driver'], $val['params']); + } + + unset($params['stack']); + + parent::__construct($params); + } + + /** + * Open the backend. + * + * @param string $save_path The path to the session object. + * @param string $session_name The name of the session. + * + * @throws Horde_SessionHandler_Exception + */ + protected function _open($save_path = null, $session_name = null) + { + foreach ($this->_stack as $val) { + $val->open($save_path, $session_name); + } + } + + /** + * Close the backend. + * + * @throws Horde_SessionHandler_Exception + */ + protected function _close() + { + foreach ($this->_stack as $val) { + $val->close(); + } + } + + /** + * Read the data for a particular session identifier from the backend. + * + * @param string $id The session identifier. + * + * @return string The session data. + */ + protected function _read($id) + { + foreach ($this->_stack as $val) { + $result = $val->read($id); + if ($result === false) { + break; + } + } + + return $result; + } + + /** + * Write session data to the backend. + * + * @param string $id The session identifier. + * @param string $session_data The session data. + * + * @return boolean True on success, false otherwise. + */ + protected function _write($id, $session_data) + { + /* Do writes in *reverse* order - it is OK if a write to one of the + * non-masters backend fails. */ + $master = true; + + foreach (array_reverse($this->_stack) as $val) { + $result = $val->write($id, $session_data); + if ($result === false) { + if ($master) { + return false; + } + /* Attempt to invalidate cache if write failed. */ + $val->destroy($id); + } + $master = false; + } + + return true; + } + + /** + * Destroy the data for a particular session identifier in the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param string $id The session identifier. + * + * @return boolean True on success, false otherwise. + */ + public function destroy($id) + { + foreach ($this->_stack as $val) { + $result = $val->destroy($id); + if ($result === false) { + return false; + } + } + + return true; + } + + /** + * Garbage collect stale sessions from the backend. + * This method should only be called internally by PHP via + * session_set_save_handler(). + * + * @param integer $maxlifetime The maximum age of a session. + * + * @return boolean True on success, false otherwise. + */ + public function gc($maxlifetime = 300) + { + /* Only report GC results from master. */ + foreach ($this->_stack as $val) { + $result = $val->gc($maxlifetime); + } + + return $result; + } + + /** + * Get a list of the valid session identifiers. + * + * @return array A list of valid session identifiers. + * @throws Horde_SessionHandler_Exception + */ + public function getSessionIDs() + { + /* Grab session ID list from the master. */ + $ob = end($this->_stack); + return $ob->getSessionIDs(); + } + +} diff --git a/framework/SessionHandler/package.xml b/framework/SessionHandler/package.xml index 55382562a..c80d864ba 100644 --- a/framework/SessionHandler/package.xml +++ b/framework/SessionHandler/package.xml @@ -37,7 +37,13 @@ http://pear.php.net/dtd/package-2.0.xsd"> beta LGPL - * Removed Horde-specific session counting script. + * Abstracted memcache persistent-backend code into 'Stack' driver. + * Renamed 'none' driver to 'Builtin'. + * Now throws Horde_SessionHandler_Exception. + * Split driver code into abstract class. + * Use horde/Db to access SQL databases. + * Do not rely on Horde::logMessage(). + * Removed Horde-specific session counting script. * Initial Horde 4 package. @@ -45,13 +51,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + - - - + @@ -64,28 +72,43 @@ http://pear.php.net/dtd/package-2.0.xsd"> 5.2.0 - 1.5.0 + 1.7.0 + + Exception + pear.horde.org + - Memcache + Auth pear.horde.org - SQL + Db + pear.horde.org + + + Log + pear.horde.org + + + Memcache pear.horde.org + + + + - - + diff --git a/horde/admin/sessions.php b/horde/admin/sessions.php index d3cd54380..7a8891629 100644 --- a/horde/admin/sessions.php +++ b/horde/admin/sessions.php @@ -22,10 +22,6 @@ require HORDE_TEMPLATES . '/admin/menu.inc'; echo '

' . _("Current Sessions"); try { - if (!isset($registry->sessionHandler)) { - throw new Horde_Exception(_("Session handler does not support listing active sessions.")); - } - $session_info = $registry->sessionHandler->getSessionsInfo(); echo ' (' . count($session_info) . ')

' . diff --git a/horde/config/conf.xml b/horde/config/conf.xml index 226454556..d947cc55f 100644 --- a/horde/config/conf.xml +++ b/horde/config/conf.xml @@ -920,17 +920,17 @@ - + localhost - - - - 3 + + + + 3 - 2 - 3 + 2 + 3 false @@ -1284,8 +1284,8 @@ Custom Session Handler Settings none - Builtin + If you have configured a custom session extension in php.ini, such as @@ -1294,7 +1294,7 @@ settings settings will be deferred to. - + @@ -1304,112 +1304,25 @@ - - - - false - true - unix - - - - - 3306 - - - - localhost - horde - - horde - - - - - - - false - horde - horde - - - - - - - - false - unix - - - - - 5432 - - - - localhost - horde - - horde - - - - + - false - unix - - - - - 5432 - - - - localhost - horde - - horde - + localhost + 389 + + + 3 + + 2 + 3 + + - + @@ -1424,6 +1337,14 @@ + + + + + + + false