From 4df4ab500788f0792b75baf1fa98e4647d713ed1 Mon Sep 17 00:00:00 2001 From: corbosman Date: Thu, 19 Feb 2015 14:55:09 +0100 Subject: session refactor and add redis driver --- program/lib/Roundcube/rcube_session_db.php | 168 +++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 program/lib/Roundcube/rcube_session_db.php (limited to 'program/lib/Roundcube/rcube_session_db.php') diff --git a/program/lib/Roundcube/rcube_session_db.php b/program/lib/Roundcube/rcube_session_db.php new file mode 100644 index 000000000..93d5c2b66 --- /dev/null +++ b/program/lib/Roundcube/rcube_session_db.php @@ -0,0 +1,168 @@ + | + | Author: Aleksander Machniak | + | Author: Cor Bosman | + +-----------------------------------------------------------------------+ +*/ + +/** + * Class to provide database session storage + * + * @package Framework + * @subpackage Core + * @author Thomas Bruederli + * @author Aleksander Machniak + * @author Cor Bosman + */ +class rcube_session_db extends rcube_session +{ + private $db; + private $table_name; + + public function __construct() + { + // get db instance + $this->db = rcube::get_instance()->get_dbh(); + + // session table name + $this->table_name = $this->db->table_name('session', true); + + // register sessions handler + $this->register_session_handler(); + + // register db gc handler + $this->register_gc_handler(array($this, 'gc_db')); + } + + /** + * @param $save_path + * @param $session_name + * @return bool + */ + public function open($save_path, $session_name) + { + return true; + } + + /** + * @return bool + */ + public function close() + { + return true; + } + + + /** + * Handler for session_destroy() + * + * @param $key + * @return bool + */ + public function destroy($key) + { + if ($key) { + $this->db->query("DELETE FROM {$this->table_name} WHERE `sess_id` = ?", $key); + } + + return true; + } + + /** + * Read session data from database + * + * @param string Session ID + * + * @return string Session vars + */ + public function read($key) + { + $sql_result = $this->db->query( + "SELECT `vars`, `ip`, `changed`, " . $this->db->now() . " AS ts" + . " FROM {$this->table_name} WHERE `sess_id` = ?", $key); + + if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { + $this->time_diff = time() - strtotime($sql_arr['ts']); + $this->changed = strtotime($sql_arr['changed']); + $this->ip = $sql_arr['ip']; + $this->vars = base64_decode($sql_arr['vars']); + $this->key = $key; + + return !empty($this->vars) ? (string) $this->vars : ''; + } + return null; + } + + /** + * insert new data into db session store + * + * @param $key + * @param $vars + * @return bool + */ + public function write($key, $vars) + { + $now = $this->db->now(); + + $this->db->query("INSERT INTO {$this->table_name}" + . " (`sess_id`, `vars`, `ip`, `created`, `changed`)" + . " VALUES (?, ?, ?, $now, $now)", + $key, base64_encode($vars), (string)$this->ip); + + return true; + } + + + /** + * update session data + * + * @param $key + * @param $newvars + * @param $oldvars + * + * @return bool + */ + public function update($key, $newvars, $oldvars) + { + $now = $this->db->now(); + + // if new and old data are not the same, update data + // else update expire timestamp only when certain conditions are met + if ($newvars !== $oldvars) { + $this->db->query("UPDATE {$this->table_name} " + . "SET `changed` = $now, `vars` = ? WHERE `sess_id` = ?", + base64_encode($newvars), $key); + } + else if ($ts - $this->changed + $this->time_diff > $this->lifetime / 2) { + $this->db->query("UPDATE {$this->table_name} SET `changed` = $now" + . " WHERE `sess_id` = ?", $key); + } + + return true; + } + + /** + * Clean up db sessions. + */ + public function gc_db() + { + // just clean all old sessions when this GC is called + $this->db->query("DELETE FROM " . $this->db->table_name('session') + . " WHERE changed < " . $this->db->now(-$this->gc_enabled)); + } + +} \ No newline at end of file -- cgit v1.2.3 From b4be89bdac46af2b1370ea25268159c2cf2cc632 Mon Sep 17 00:00:00 2001 From: corbosman Date: Fri, 27 Feb 2015 15:03:58 +0100 Subject: use factory --- program/lib/Roundcube/rcube.php | 41 ++------------- program/lib/Roundcube/rcube_session.php | 67 ++++++++++++++++++++---- program/lib/Roundcube/rcube_session_db.php | 7 ++- program/lib/Roundcube/rcube_session_memcache.php | 12 +++-- program/lib/Roundcube/rcube_session_php.php | 12 +++-- program/lib/Roundcube/rcube_session_redis.php | 20 +++---- 6 files changed, 95 insertions(+), 64 deletions(-) (limited to 'program/lib/Roundcube/rcube_session_db.php') diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 42d880763..f15ae840a 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -521,49 +521,18 @@ class rcube ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); - // use database for storing session data - $storage = $this->config->get('session_storage', 'db'); - $this->session = $this->get_session($storage); + // get storage driver from config + // $storage = $this->config->get('session_storage', 'db'); - // register default gc handler - $this->session->register_gc_handler(array($this, 'gc')); - - $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); - $this->session->set_ip_check($this->config->get('ip_check')); - - if ($this->config->get('session_auth_name')) { - $this->session->set_cookiename($this->config->get('session_auth_name')); - } + // get session driver instance + $this->session = rcube_session::factory($this->config); // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { - $this->session->start($this->config); + $this->session->start(); } } - /** - * get an rcube_session instance - * - * @return rcube_session - */ - private function get_session($storage) - { - // class name for this storage - $class = "rcube_session_" . $storage; - - // try to instantiate class - if(class_exists($class)) { - return new $class(); - } - - // no storage found, raise error - rcube::raise_error(array('code' => 604, 'type' => 'session', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => "Failed to find session driver. Check session_storage config option"), - true, true); - } - - /** * Garbage collector - cache/temp cleaner */ diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index 08a9dc302..fc1d87150 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -15,6 +15,7 @@ +-----------------------------------------------------------------------+ | Author: Thomas Bruederli | | Author: Aleksander Machniak | + | Author: Cor Bosman | +-----------------------------------------------------------------------+ */ @@ -43,6 +44,7 @@ abstract class rcube_session protected $secret = ''; protected $ip_check = false; protected $logging = false; + protected $config; /** * Blocks session data from being written to database. @@ -50,7 +52,55 @@ abstract class rcube_session * @var boolean */ public $nowrite = false; - + + /** + * Factory, returns driver-specific instance of the class + * + * @param object $config + * @return Object rcube_session + */ + public static function factory($config) + { + // get session storage driver + $storage = $config->get('session_storage', 'db'); + + // class name for this storage + $class = "rcube_session_" . $storage; + + // try to instantiate class + if (class_exists($class)) { + return new $class($config); + } + + // no storage found, raise error + rcube::raise_error(array('code' => 604, 'type' => 'session', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Failed to find session driver. Check session_storage config option"), + true, true); + } + + /** + * @param Object $config + */ + public function __construct($config) + { + $this->config = $config; + + // register default gc handler + $this->register_gc_handler(array($this, 'gc')); + + // set secret + $this->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); + + // set ip check + $this->set_ip_check($this->config->get('ip_check')); + + // set cookie name + if ($this->config->get('session_auth_name')) { + $this->set_cookiename($this->config->get('session_auth_name')); + } + } + /** * register session handler */ @@ -73,13 +123,13 @@ abstract class rcube_session /** * Wrapper for session_start() */ - public function start($config) + public function start() { $this->start = microtime(true); $this->ip = rcube_utils::remote_addr(); - $this->logging = $config->get('log_session', false); + $this->logging = $this->config->get('log_session', false); - $lifetime = $config->get('session_lifetime', 1) * 60; + $lifetime = $this->config->get('session_lifetime', 1) * 60; $this->set_lifetime($lifetime); session_start(); @@ -105,8 +155,9 @@ abstract class rcube_session */ public function sess_write($key, $vars) { - if ($this->nowrite) + if ($this->nowrite) { return true; + } // check cache $oldvars = $this->get_cache($key); @@ -201,12 +252,6 @@ abstract class rcube_session protected function gc_shutdown() { if ($this->gc_enabled) { - // just delete all expired sessions - if ($this->storage == 'db') { - $this->db->query("DELETE FROM {$this->table_name}" - . " WHERE `changed` < " . $this->db->now(-$this->gc_enabled)); - } - foreach ($this->gc_handlers as $fct) { call_user_func($fct); } diff --git a/program/lib/Roundcube/rcube_session_db.php b/program/lib/Roundcube/rcube_session_db.php index 93d5c2b66..feba2e083 100644 --- a/program/lib/Roundcube/rcube_session_db.php +++ b/program/lib/Roundcube/rcube_session_db.php @@ -33,8 +33,13 @@ class rcube_session_db extends rcube_session private $db; private $table_name; - public function __construct() + /** + * @param Object $config + */ + public function __construct($config) { + parent::__construct($config); + // get db instance $this->db = rcube::get_instance()->get_dbh(); diff --git a/program/lib/Roundcube/rcube_session_memcache.php b/program/lib/Roundcube/rcube_session_memcache.php index 85a4aa617..732d5fb7a 100644 --- a/program/lib/Roundcube/rcube_session_memcache.php +++ b/program/lib/Roundcube/rcube_session_memcache.php @@ -15,7 +15,7 @@ +-----------------------------------------------------------------------+ | Author: Thomas Bruederli | | Author: Aleksander Machniak | - | Author: Cor Bosman | + | Author: Cor Bosman | +-----------------------------------------------------------------------+ */ @@ -32,11 +32,16 @@ class rcube_session_memcache extends rcube_session { private $memcache; - public function __construct() + /** + * @param Object $config + */ + public function __construct($config) { + parent::__construct($config); + $this->memcache = rcube::get_instance()->get_memcache(); - if(! $this->memcache) { + if (!$this->memcache) { rcube::raise_error(array('code' => 604, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => "Failed to connect to memcached. Please check configuration"), @@ -45,7 +50,6 @@ class rcube_session_memcache extends rcube_session // register sessions handler $this->register_session_handler(); - } /** diff --git a/program/lib/Roundcube/rcube_session_php.php b/program/lib/Roundcube/rcube_session_php.php index 73a889259..2f7085fc7 100644 --- a/program/lib/Roundcube/rcube_session_php.php +++ b/program/lib/Roundcube/rcube_session_php.php @@ -30,7 +30,6 @@ */ class rcube_session_php extends rcube_session { - /** * native php sessions don't need a save handler * we do need to define abstract function implementations but they are not used. @@ -43,6 +42,13 @@ class rcube_session_php extends rcube_session { public function write($key, $vars) {} public function update($key, $newvars, $oldvars) {} + /** + * @param Object $config + */ + public function __construct($config) + { + parent::__construct($config); + } /** * Wrapper for session_write_close() @@ -58,9 +64,9 @@ class rcube_session_php extends rcube_session { /** * Wrapper for session_start() */ - public function start($config) + public function start() { - parent::start($config); + parent::start(); $this->key = session_id(); $this->ip = $_SESSION['__IP']; diff --git a/program/lib/Roundcube/rcube_session_redis.php b/program/lib/Roundcube/rcube_session_redis.php index 07a91cc45..bc545ca95 100644 --- a/program/lib/Roundcube/rcube_session_redis.php +++ b/program/lib/Roundcube/rcube_session_redis.php @@ -13,8 +13,6 @@ | PURPOSE: | | Provide database supported session management | +-----------------------------------------------------------------------+ - | Author: Thomas Bruederli | - | Author: Aleksander Machniak | | Author: Cor Bosman | +-----------------------------------------------------------------------+ */ @@ -30,12 +28,17 @@ class rcube_session_redis extends rcube_session { private $redis; - public function __construct() + /** + * @param Object $config + */ + public function __construct($config) { + parent::__construct($config); + // instantiate Redis object $this->redis = new Redis(); - if (! $this->redis) { + if (!$this->redis) { rcube::raise_error(array('code' => 604, 'type' => 'session', 'line' => __LINE__, 'file' => __FILE__, 'message' => "Failed to find Redis. Make sure php-redis is included"), @@ -43,10 +46,10 @@ class rcube_session_redis extends rcube_session { } // get config instance - $hosts = rcube::get_instance()->config->get('redis_hosts', array()); + $hosts = $this->config->get('redis_hosts', array('localhost')); // host config is wrong - if (!is_array($hosts) || empty($hosts) ) { + if (!is_array($hosts) || empty($hosts)) { rcube::raise_error(array('code' => 604, 'type' => 'session', 'line' => __LINE__, 'file' => __FILE__, 'message' => "Redis host not configured"), @@ -61,9 +64,9 @@ class rcube_session_redis extends rcube_session { true, true); } - foreach($hosts as $config) { + foreach ($hosts as $host) { // explode individual fields - list($host, $port, $database, $password) = array_pad(explode(':', $config, 4), 4, null); + list($host, $port, $database, $password) = array_pad(explode(':', $host, 4), 4, null); // set default values if not set $host = ($host !== null) ? $host : '127.0.0.1'; @@ -115,7 +118,6 @@ class rcube_session_redis extends rcube_session { // register sessions handler $this->register_session_handler(); - } /** -- cgit v1.2.3 From 82058d7af26ff04fd95442815b93f944cea46f10 Mon Sep 17 00:00:00 2001 From: corbosman Date: Wed, 4 Mar 2015 13:56:37 +0100 Subject: minor fixes --- program/lib/Roundcube/rcube.php | 2 +- program/lib/Roundcube/rcube_session_db.php | 2 +- program/lib/Roundcube/rcube_session_redis.php | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'program/lib/Roundcube/rcube_session_db.php') diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 3aa461e36..a7c6b9317 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -520,7 +520,7 @@ class rcube ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); - + // get session driver instance $this->session = rcube_session::factory($this->config); diff --git a/program/lib/Roundcube/rcube_session_db.php b/program/lib/Roundcube/rcube_session_db.php index feba2e083..78138d1eb 100644 --- a/program/lib/Roundcube/rcube_session_db.php +++ b/program/lib/Roundcube/rcube_session_db.php @@ -41,7 +41,7 @@ class rcube_session_db extends rcube_session parent::__construct($config); // get db instance - $this->db = rcube::get_instance()->get_dbh(); + $this->db = rcube::get_instance()->get_dbh(); // session table name $this->table_name = $this->db->table_name('session', true); diff --git a/program/lib/Roundcube/rcube_session_redis.php b/program/lib/Roundcube/rcube_session_redis.php index bc545ca95..4822db7f9 100644 --- a/program/lib/Roundcube/rcube_session_redis.php +++ b/program/lib/Roundcube/rcube_session_redis.php @@ -4,14 +4,13 @@ +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | | Copyright (C) 2005-2014, The Roundcube Dev Team | - | Copyright (C) 2011, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | | See the README file for a full license statement. | | | | PURPOSE: | - | Provide database supported session management | + | Provide redis supported session management | +-----------------------------------------------------------------------+ | Author: Cor Bosman | +-----------------------------------------------------------------------+ -- cgit v1.2.3 From 87ff88d55003a7af755d290ae06173c4d73cc133 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 9 Mar 2015 17:16:39 +0100 Subject: Fix session garbage collector handler registration after refactoring --- program/lib/Roundcube/rcube.php | 1 + program/lib/Roundcube/rcube_session.php | 3 --- program/lib/Roundcube/rcube_session_db.php | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'program/lib/Roundcube/rcube_session_db.php') diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 3d081539f..cf6ebf993 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -523,6 +523,7 @@ class rcube // get session driver instance $this->session = rcube_session::factory($this->config); + $this->session->register_gc_handler(array($this, 'gc')); // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index fc1d87150..ab5c24c1e 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -86,9 +86,6 @@ abstract class rcube_session { $this->config = $config; - // register default gc handler - $this->register_gc_handler(array($this, 'gc')); - // set secret $this->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); diff --git a/program/lib/Roundcube/rcube_session_db.php b/program/lib/Roundcube/rcube_session_db.php index 78138d1eb..4d94f43ff 100644 --- a/program/lib/Roundcube/rcube_session_db.php +++ b/program/lib/Roundcube/rcube_session_db.php @@ -168,6 +168,7 @@ class rcube_session_db extends rcube_session // just clean all old sessions when this GC is called $this->db->query("DELETE FROM " . $this->db->table_name('session') . " WHERE changed < " . $this->db->now(-$this->gc_enabled)); + $this->log("Session GC (DB): remove records < " . date('Y-m-d H:i:s', time() - $this->gc_enabled) . '; rows = ' . intval($this->db->affected_rows())); } } \ No newline at end of file -- cgit v1.2.3 From 8eeb721bd69e602137d93b0dfecc33752b078679 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 13 Mar 2015 10:28:06 +0100 Subject: Fix unintended DB session expiration - lost variable in refactoring (#1490316) --- program/lib/Roundcube/rcube_session_db.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube/rcube_session_db.php') diff --git a/program/lib/Roundcube/rcube_session_db.php b/program/lib/Roundcube/rcube_session_db.php index 4d94f43ff..a814c2b9b 100644 --- a/program/lib/Roundcube/rcube_session_db.php +++ b/program/lib/Roundcube/rcube_session_db.php @@ -143,7 +143,8 @@ class rcube_session_db extends rcube_session */ public function update($key, $newvars, $oldvars) { - $now = $this->db->now(); + $now = $this->db->now(); + $ts = microtime(true); // if new and old data are not the same, update data // else update expire timestamp only when certain conditions are met -- cgit v1.2.3