SessionHandler rewrite.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 14 May 2010 23:30:14 +0000 (17:30 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 18 May 2010 17:39:04 +0000 (11:39 -0600)
* 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).

18 files changed:
framework/Core/lib/Horde/Core/Binder/SessionHandler.php
framework/Core/lib/Horde/Registry.php
framework/SessionHandler/lib/Horde/SessionHandler.php
framework/SessionHandler/lib/Horde/SessionHandler/Builtin.php [new file with mode: 0644]
framework/SessionHandler/lib/Horde/SessionHandler/Driver.php [new file with mode: 0644]
framework/SessionHandler/lib/Horde/SessionHandler/Exception.php [new file with mode: 0644]
framework/SessionHandler/lib/Horde/SessionHandler/External.php [new file with mode: 0644]
framework/SessionHandler/lib/Horde/SessionHandler/Ldap.php
framework/SessionHandler/lib/Horde/SessionHandler/Memcache.php
framework/SessionHandler/lib/Horde/SessionHandler/Mysql.php [deleted file]
framework/SessionHandler/lib/Horde/SessionHandler/None.php [deleted file]
framework/SessionHandler/lib/Horde/SessionHandler/Oci8.php [deleted file]
framework/SessionHandler/lib/Horde/SessionHandler/Pgsql.php [deleted file]
framework/SessionHandler/lib/Horde/SessionHandler/Sql.php
framework/SessionHandler/lib/Horde/SessionHandler/Stack.php [new file with mode: 0644]
framework/SessionHandler/package.xml
horde/admin/sessions.php
horde/config/conf.xml

index f554f5b..c489cf9 100644 (file)
@@ -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);
     }
 
index fa81333..d4f6fac 100644 (file)
@@ -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');
     }
 
     /**
index a52ecda..ce26e01 100644 (file)
  * Horde_SessionHandler:: defines an API for implementing custom PHP session
  * handlers.
  *
- * Optional parameters:<pre>
- * 'memcache' - (Horde_Memcache) If set, uses memcache to cache session
- *              data.
- * </pre>
- *
  * 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 <mike@graftonhall.co.nz>
- * @author  Michael Slusarz <slusarz@curecanti.org>
- * @package Horde_SessionHandler
+ * @author   Mike Cochrane <mike@graftonhall.co.nz>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @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 (file)
index 0000000..c0634bb
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * SessionHandler:: implementation for PHP's built-in session handler.
+ * This doesn't do any session handling itself - instead, it exists to allow
+ * utility features to be used with the built-in PHP handler.
+ *
+ * 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 <selsky@columbia.edu>
+ * @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 (file)
index 0000000..fa78adc
--- /dev/null
@@ -0,0 +1,321 @@
+<?php
+/**
+ * Horde_SessionHandler_Driver:: is the abstract class that all drivers
+ * inherit from.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @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:
+     * <pre>
+     * 'logger' - (Horde_Log_Logger) A logger instance.
+     *            DEFAULT: No logging
+     * 'noset' - (boolean) If true, don't set the save handler.
+     *           DEFAULT: false
+     * </pre>
+     */
+    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 (file)
index 0000000..b998f86
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Exception handler for the SessionHandler package.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @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 (file)
index 0000000..147e4a5
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Horde_SessionHandler_External:: implements an external save handler defined
+ * via driver configuration parameters.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  SessionHandler
+ */
+class Horde_SessionHandler_External extends Horde_SessionHandler_Driver
+{
+    /**
+     * Constructor.
+     *
+     * @param array $params  Required parameters:
+     * <pre>
+     * '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().
+     * </pre>
+     *
+     * @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.');
+    }
+
+}
index a3e7767..aebee03 100644 (file)
@@ -1,23 +1,19 @@
 <?php
 /**
- * Horde_SessionHandler:: implementation for LDAP directories.
- *
- * Required parameters:<pre>
- *   '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.
- * </pre>
+ * 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:
+     * <pre>
+     * '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
+     * </pre>
+     *
+     * @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.');
+    }
+
 }
index e92cc11..ec31380 100644 (file)
@@ -1,31 +1,18 @@
 <?php
 /**
- * Horde_SessionHandler implementation for memcache.
- *
- * Required parameters:<pre>
- * 'memcache' - (Horde_Memcache) A memcache object.
- * </pre>
- *
- * Optional parameters:<pre>
- * '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.
- * </pre>
+ * 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 <rafan@infor.org>
- * @author  Michael Slusarz <slusarz@curecanti.org>
- * @package Horde_SessionHandler
+ * @author   Rong-En Fan <rafan@infor.org>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @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:
+     * <pre>
+     * '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.
+     * </pre>
      *
-     * @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 (file)
index 901e62d..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-<?php
-/**
- * Horde_SessionHandler:: implementation for MySQL (native).
- *
- * Required parameters:<pre>
- *   '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).
- * </pre>
- *
- * Required for some configurations:<pre>
- *   'port' - (integer) The port on which to connect to the database.
- * </pre>
- *
- * Optional parameters:<pre>
- *   'persistent' - (boolean) Use persistent DB connections?
- * </pre>
- *
- * 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 <mike@graftonhall.co.nz>
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @author  Jan Schneider <jan@horde.org>
- * @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 (file)
index 1a6be3c..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * Horde_SessionHandler:: implementation for PHP's built-in session handler.
- *
- * Required parameters:<pre>
- *   None.</pre>
- *
- * Optional parameters:<pre>
- *   None.</pre>
- *
- * 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 <selsky@columbia.edu>
- * @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 (file)
index 4939dd5..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-<?php
-/**
- * Horde_SessionHandler:: implementation for Oracle 8i (native).
- *
- * Required parameters:<pre>
- *   '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'.
- * </pre>
- *
- * Required for some configurations:<pre>
- *   'port' - (integer) The port on which to connect to the database.
- * </pre>
- *
- * Optional parameters:<pre>
- *   'persistent' - (boolean) Use persistent DB connections?
- * </pre>
-
- * The table structure can be found in:
- *   horde/scripts/sql/horde_sessionhandler.oci8.sql.
- *
- * Copyright 2003-2009 Liam Hoekenga <liamr@umich.edu>
- *
- * 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 <liamr@umich.edu>
- * @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 (file)
index ad164b7..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-<?php
-/**
- * Horde_SessionHandler implementation for PostgreSQL (native).
- *
- * Copyright 1999-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.
- *
- * Required parameters:<pre>
- *   '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'):<pre>
- *   'hostspec' - (string) The hostname of the database server.
- *   'port'     - (integer) The port on which to connect to the database.
- * </pre>
- *
- * Optional parameters:<pre>
- *   'persistent' - (boolean) Use persistent DB connections?
- *                  Default: NO
- *   'table'      - (string) The name of the sessiondata table in 'database'.
- *                  Default: 'horde_sessionhandler'</pre>
- * </pre>
-
- * The table structure can be found in:
- *   horde/scripts/sql/horde_sessionhandler.pgsql.sql.
- *
- * Contributors:<pre>
- *  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</pre>
- *
- * @author  Jon Parise <jon@csh.rit.edu>
- * @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) . "'";
-    }
-
-}
index 0be3fa7..0bbbad8 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 /**
- * Horde_SessionHandler implementation for PHP's PEAR database abstraction
- * layer.
+ * SessionHandler implementation for SQL databases.
  *
  * The table structure can be found in:
  *   horde/scripts/sql/horde_sessionhandler.sql.
  * 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 <mike@graftonhall.co.nz>
- * @package Horde_SessionHandler
+ * @author   Mike Cochrane <mike@graftonhall.co.nz>
+ * @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:
      * <pre>
-     * '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'
      * </pre>
      *
-     * @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 (file)
index 0000000..c06e944
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Horde_SessionHandler_Stack:: is an implementation that will loop through
+ * a given list of Horde_SessionHandler_Drivers to return the session
+ * information.  This driver allows for use of caching backends on top of
+ * persistent backends.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @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:
+     * <pre>
+     * '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.
+     * </pre>
+     *
+     * @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();
+    }
+
+}
index 5538256..c80d864 100644 (file)
@@ -37,7 +37,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <api>beta</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Removed Horde-specific session counting script.
+ <notes>* 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.
  </notes>
  <contents>
@@ -45,13 +51,15 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <dir name="lib">
     <dir name="Horde">
      <dir name="SessionHandler">
+      <file name="Builtin.php" role="php" />
+      <file name="Driver.php" role="php" />
+      <file name="Exception.php" role="php" />
+      <file name="External.php" role="php" />
       <file name="Ldap.php" role="php" />
       <file name="Memcache.php" role="php" />
-      <file name="Mysql.php" role="php" />
       <file name="None.php" role="php" />
-      <file name="Oci8.php" role="php" />
-      <file name="Pgsql.php" role="php" />
       <file name="Sql.php" role="php" />
+      <file name="Stack.php" role="php" />
      </dir> <!-- /lib/Horde/SessionHandler -->
      <file name="SessionHandler.php" role="php" />
     </dir> <!-- /lib/Horde -->
@@ -64,28 +72,43 @@ http://pear.php.net/dtd/package-2.0.xsd">
     <min>5.2.0</min>
    </php>
    <pearinstaller>
-    <min>1.5.0</min>
+    <min>1.7.0</min>
    </pearinstaller>
+   <package>
+    <name>Exception</name>
+    <channel>pear.horde.org</channel>
+   </package>
   </required>
   <optional>
    <package>
-    <name>Memcache</name>
+    <name>Auth</name>
     <channel>pear.horde.org</channel>
    </package>
    <package>
-    <name>SQL</name>
+    <name>Db</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Log</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Memcache</name>
     <channel>pear.horde.org</channel>
    </package>
   </optional>
  </dependencies>
  <phprelease>
   <filelist>
+   <install name="lib/Horde/SessionHandler/Builtin.php" as="Horde/SessionHandler/Builtin.php" />
+   <install name="lib/Horde/SessionHandler/Driver.php" as="Horde/SessionHandler/Driver.php" />
+   <install name="lib/Horde/SessionHandler/Exception.php" as="Horde/SessionHandler/Exception.php" />
+   <install name="lib/Horde/SessionHandler/External.php" as="Horde/SessionHandler/External.php" />
    <install name="lib/Horde/SessionHandler/Ldap.php" as="Horde/SessionHandler/Ldap.php" />
    <install name="lib/Horde/SessionHandler/Memcache.php" as="Horde/SessionHandler/Memcache.php" />
-   <install name="lib/Horde/SessionHandler/Mysql.php" as="Horde/SessionHandler/Mysql.php" />
    <install name="lib/Horde/SessionHandler/None.php" as="Horde/SessionHandler/None.php" />
-   <install name="lib/Horde/SessionHandler/Oci8.php" as="Horde/SessionHandler/Pgsql.php" />
    <install name="lib/Horde/SessionHandler/Sql.php" as="Horde/SessionHandler/Sql.php" />
+   <install name="lib/Horde/SessionHandler/Stack.php" as="Horde/SessionHandler/Stack.php" />
    <install name="lib/Horde/SessionHandler.php" as="Horde/SessionHandler.php" />
   </filelist>
  </phprelease>
index d3cd543..7a88916 100644 (file)
@@ -22,10 +22,6 @@ require HORDE_TEMPLATES . '/admin/menu.inc';
 
 echo '<h1 class="header">' . _("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) . ')</h1>' .
index 2264545..d947cc5 100644 (file)
      <case name="kolab" desc="Kolab"/>
      <case name="ldap" desc="LDAP">
        <configsection name="params">
-        <configstring name="hostspec" desc="The hostname of the LDAP server">
+        <configstring name="hostspec" desc="the hostname of the ldap server">
         localhost</configstring>
-        <configstring name="basedn" desc="The base DN for the LDAP server"/>
-        <configstring name="binddn" required="false" desc="The DN used to bind
-        to the LDAP server"/>
-        <configstring name="password" required="false" desc="The password used
-        to bind to the LDAP server"/>
-        <configenum name="version" desc="LDAP protocol version">3
+        <configstring name="basedn" desc="the base dn for the ldap server"/>
+        <configstring name="binddn" required="false" desc="the dn used to bind
+        to the ldap server"/>
+        <configstring name="password" required="false" desc="the password used
+        to bind to the ldap server"/>
+        <configenum name="version" desc="ldap protocol version">3
          <values>
-          <value desc="LDAPv2 (deprecated)">2</value>
-          <value desc="LDAPv3">3</value>
+          <value desc="ldapv2 (deprecated)">2</value>
+          <value desc="ldapv3">3</value>
          </values>
         </configenum>
         <configboolean name="tls" desc="Enable TLS?">false</configboolean>
   <configsection name="sessionhandler">
    <configheader>Custom Session Handler Settings</configheader>
    <configswitch name="type" desc="What sessionhandler driver should we
-   use?">none
-    <case name="none" desc="Use the default PHP session handler (file-based by
+   use?">Builtin
+    <case name="Builtin" desc="Use the default PHP session handler (file-based by
     default)">
      <configdescription>
       If you have configured a custom session extension in php.ini, such as
       settings settings will be deferred to.
      </configdescription>
     </case>
-    <case name="external" desc="Use your own custom session handler">
+    <case name="External" desc="Use your own custom session handler">
      <configsection name="params">
       <configstring name="open" desc="Your open() function"/>
       <configstring name="close" desc="Your close() function"/>
       <configstring name="gc" desc="Your gc() function"/>
      </configsection>
     </case>
-
-    <case name="mysql" desc="MySQL based sessions">
-     <configsection name="params">
-      <configboolean name="persistent" desc="Request persistent
-      connections?">false</configboolean>
-      <configboolean name="rowlocking" desc="Should we use row-level locking
-      and transactions? This is strongly recommended, but requires a table
-      type that is transaction-safe and supports row-level locking, like
-      InnoDB. If you don't have such a table type, disable this setting and we
-      will use table-level locking and no locking
-      instead.">true</configboolean>
-      <configswitch name="protocol" desc="What protocol will we use to connect
-      to the database?">unix
-       <case name="unix" desc="UNIX Sockets">
-        <configstring name="socket" required="false" desc="Location of UNIX
-        socket"></configstring>
-       </case>
-       <case name="tcp" desc="TCP/IP">
-        <configinteger name="port" required="false" desc="Port the DB is
-        running on, if non-standard">3306</configinteger>
-       </case>
-      </configswitch>
-      <configstring name="hostspec" desc="What hostname is the database server
-      running on, or what is the name of the system DSN to use?">
-      localhost</configstring>
-      <configstring name="username" desc="What username do we authenticate to
-      the database server as?">horde</configstring>
-      <configstring name="password" required="false" desc="What password do we
-      authenticate to the database server with?"/>
-      <configstring name="database" desc="What database name/tablespace are we
-      using?">horde</configstring>
-      <configstring name="table" required="false" desc="The name of the
-      session table in the database [horde_sessionhandler]"/>
-     </configsection>
-    </case>
-    <case name="oci8" desc="Oracle based sessions">
-     <configsection name="params">
-      <configboolean name="persistent" desc="Request persistent connections?">
-      false</configboolean>
-      <configstring name="hostspec" desc="Database name or Easy Connect
-      parameter">horde</configstring>
-      <configstring name="username" desc="What username do we authenticate to
-      the database server as?">horde</configstring>
-      <configstring name="password" required="false" desc="What password do we
-      authenticate to the database server with?"/>
-      <configstring name="table" required="false" desc="The name of the session
-      table in the database [horde_sessionhandler]"/>
-     </configsection>
-    </case>
-    <case name="pgsql" desc="PostgreSQL based sessions">
-     <configsection name="params">
-      <configboolean name="persistent" desc="Request persistent connections?">
-      false</configboolean>
-      <configswitch name="protocol" desc="What protocol will we use to connect
-      to the database?">unix
-       <case name="unix" desc="UNIX Sockets">
-        <configstring name="socket" required="false" desc="Location of UNIX
-        socket"></configstring>
-       </case>
-       <case name="tcp" desc="TCP/IP">
-        <configinteger name="port" required="false" desc="Port the DB is
-        running on, if non-standard">5432</configinteger>
-       </case>
-      </configswitch>
-      <configstring name="hostspec" desc="What hostname is the database server
-      running on, or what is the name of the system DSN to use?">
-      localhost</configstring>
-      <configstring name="username" desc="What username do we authenticate to
-      the database server as?">horde</configstring>
-      <configstring name="password" required="false" desc="What password do we
-      authenticate to the database server with?"/>
-      <configstring name="database" desc="What database name/tablespace are we
-      using?">horde</configstring>
-      <configstring name="table" required="false" desc="The name of the
-      session table in the database [horde_sessionhandler]"/>
-     </configsection>
-    </case>
-    <case name="sql" desc="Use PEAR's DB abstraction layer">
+    <case name="Ldap" desc="LDAP">
      <configsection name="params">
-      <configboolean name="persistent" desc="Request persistent
-      connections?">false</configboolean>
-      <configswitch name="protocol" desc="What protocol will we use to connect
-      to the database?">unix
-       <case name="unix" desc="UNIX Sockets">
-        <configstring name="socket" required="false" desc="Location of UNIX
-        socket"></configstring>
-       </case>
-       <case name="tcp" desc="TCP/IP">
-        <configinteger name="port" required="false" desc="Port the DB is
-        running on, if non-standard">5432</configinteger>
-       </case>
-      </configswitch>
-      <configstring name="hostspec" desc="What hostname is the database server
-      running on, or what is the name of the system DSN to use?">
-      localhost</configstring>
-      <configstring name="username" desc="What username do we authenticate to
-      the database server as?">horde</configstring>
-      <configstring name="password" required="false" desc="What password do we
-      authenticate to the database server with?"/>
-      <configstring name="database" desc="What database name/tablespace are we
-      using?">horde</configstring>
-      <configstring name="table" required="false" desc="The name of the session
-      table in the database [horde_sessionhandler]"/>
+      <configstring name="hostspec" descu"LDAP
+      server/host">localhost</configstring>
+      <configinteger name="port" required="false" desc="Port LDAP is running
+      on, if non-standard">389</configinteger>
+      <configstring name="dn" required="false" desc="The DN used to bind
+      to the LDAP server"/>
+      <configstring name="password" required="false" desc="The password used
+      to bind to the LDAP server"/>
+      <configenum name="version" desc="LDAP Protocol Version">3
+       <values>
+        <value desc="LDAPv2 (Deprecated)">2</value>
+        <value desc="LDAPv3">3</value>
+       </values>
+      </configenum>
      </configsection>
     </case>
-    <case name="memcache" desc="Memcache only">
+    <case name="Memcache" desc="Memcache only">
      <configsection name="params">
       <configswitch name="track" required="false" desc="Keep track of active
       session information? Tracking requires a small amount of extra load.">
       </configswitch>
      </configsection>
     </case>
+    <case name="Sql" desc="SQL Database">
+     <configsection name="params">
+      <configsql switchname="driverconfig">
+       <configstring name="table" required="false" desc="The name of the
+       preference table in the database [horde_sessionhandler]"/>
+      </configsql>
+     </configsection>
+    </case>
    </configswitch>
    <configboolean name="memcache" desc="Use memcache to cache session
    information?">false</configboolean>