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.php | 29 +- program/lib/Roundcube/rcube_session.php | 371 ++++++----------------- program/lib/Roundcube/rcube_session_db.php | 168 ++++++++++ program/lib/Roundcube/rcube_session_memcache.php | 140 +++++++++ program/lib/Roundcube/rcube_session_php.php | 71 +++++ program/lib/Roundcube/rcube_session_redis.php | 210 +++++++++++++ 6 files changed, 711 insertions(+), 278 deletions(-) create mode 100644 program/lib/Roundcube/rcube_session_db.php create mode 100644 program/lib/Roundcube/rcube_session_memcache.php create mode 100644 program/lib/Roundcube/rcube_session_php.php create mode 100644 program/lib/Roundcube/rcube_session_redis.php (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 3aca88843..42d880763 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -522,9 +522,12 @@ class rcube ini_set('session.cookie_httponly', 1); // use database for storing session data - $this->session = new rcube_session($this->get_dbh(), $this->config); + $storage = $this->config->get('session_storage', 'db'); + $this->session = $this->get_session($storage); + // 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')); @@ -534,8 +537,30 @@ class rcube // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { - $this->session->start(); + $this->session->start($this->config); + } + } + + /** + * 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); } diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index 8306a0687..08a9dc302 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -19,33 +19,30 @@ */ /** - * Class to provide database supported session storage + * Abstract class to provide database supported session storage * * @package Framework * @subpackage Core * @author Thomas Bruederli * @author Aleksander Machniak */ -class rcube_session +abstract class rcube_session { - private $db; - private $ip; - private $start; - private $changed; - private $time_diff = 0; - private $reloaded = false; - private $appends = array(); - private $unsets = array(); - private $gc_handlers = array(); - private $cookiename = 'roundcube_sessauth'; - private $vars; - private $key; - private $now; - private $secret = ''; - private $ip_check = false; - private $logging = false; - private $storage; - private $memcache; + protected $key; + protected $ip; + protected $changed; + protected $start; + protected $time_diff = 0; + protected $reloaded = false; + protected $appends = array(); + protected $unsets = array(); + protected $gc_handlers = array(); + protected $cookiename = 'roundcube_sessauth'; + protected $vars; + protected $now; + protected $secret = ''; + protected $ip_check = false; + protected $logging = false; /** * Blocks session data from being written to database. @@ -53,14 +50,31 @@ class rcube_session * @var boolean */ public $nowrite = false; + + /** + * register session handler + */ + public function register_session_handler() + { + ini_set('session.serialize_handler', 'php'); + + // set custom functions for PHP session management + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'sess_write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } /** - * Default constructor + * Wrapper for session_start() */ - public function __construct($db, $config) + public function start($config) { - $this->db = $db; $this->start = microtime(true); $this->ip = rcube_utils::remote_addr(); $this->logging = $config->get('log_session', false); @@ -68,83 +82,43 @@ class rcube_session $lifetime = $config->get('session_lifetime', 1) * 60; $this->set_lifetime($lifetime); - // use memcache backend - $this->storage = $config->get('session_storage', 'db'); - if ($this->storage == 'memcache') { - $this->memcache = rcube::get_instance()->get_memcache(); - - // set custom functions for PHP session management if memcache is available - if ($this->memcache) { - ini_set('session.serialize_handler', 'php'); - - session_set_save_handler( - array($this, 'open'), - array($this, 'close'), - array($this, 'mc_read'), - array($this, 'mc_write'), - array($this, 'mc_destroy'), - array($this, 'gc')); - } - else { - rcube::raise_error(array('code' => 604, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => "Failed to connect to memcached. Please check configuration"), - true, true); - } - } - else if ($this->storage != 'php') { - ini_set('session.serialize_handler', 'php'); - - // set custom functions for PHP session management - session_set_save_handler( - array($this, 'open'), - array($this, 'close'), - array($this, 'db_read'), - array($this, 'db_write'), - array($this, 'db_destroy'), - array($this, 'gc')); - - $this->table_name = $this->db->table_name('session', true); - } + session_start(); } - /** - * Wrapper for session_start() + * Abstract methods should be implemented by driver classes */ - public function start() - { - session_start(); - - // copy some session properties to object vars - if ($this->storage == 'php') { - $this->key = session_id(); - $this->ip = $_SESSION['__IP']; - $this->changed = $_SESSION['__MTIME']; - } - } - - - public function open($save_path, $session_name) - { - return true; - } - - - public function close() - { - return true; - } + abstract function open($save_path, $session_name); + abstract function close(); + abstract function destroy($key); + abstract function read($key); + abstract function write($key, $vars); + abstract function update($key, $newvars, $oldvars); /** - * Delete session data for the given key + * session write handler. This calls the implementation methods for write/update after some initial checks. * - * @param string Session ID + * @param $key + * @param $vars + * @return bool */ - public function destroy($key) + public function sess_write($key, $vars) { - return $this->memcache ? $this->mc_destroy($key) : $this->db_destroy($key); + if ($this->nowrite) + return true; + + // check cache + $oldvars = $this->get_cache($key); + + // if there are cached vars, update store, else insert new data + if ($oldvars !== null) { + $newvars = $this->_fixvars($vars, $oldvars); + return $this->update($key, $newvars, $oldvars); + } + else { + return $this->write($key, $vars); + } } @@ -153,11 +127,6 @@ class rcube_session */ public function write_close() { - if ($this->storage == 'php') { - $_SESSION['__IP'] = $this->ip; - $_SESSION['__MTIME'] = time(); - } - session_write_close(); // write_close() is called on script shutdown, see rcube::shutdown() @@ -166,91 +135,10 @@ class rcube_session $this->gc_shutdown(); } - - /** - * Read session data from database - * - * @param string Session ID - * - * @return string Session vars - */ - public function db_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; - } - - - /** - * Save session data. - * handler for session_read() - * - * @param string Session ID - * @param string Serialized session vars - * - * @return boolean True on success - */ - public function db_write($key, $vars) - { - $now = $this->db->now(); - $ts = microtime(true); - - if ($this->nowrite) - return true; - - // no session row in DB (db_read() returns false) - if (!$this->key) { - $oldvars = null; - } - // use internal data from read() for fast requests (up to 0.5 sec.) - else if ($key == $this->key && (!$this->vars || $ts - $this->start < 0.5)) { - $oldvars = $this->vars; - } - else { // else read data again from DB - $oldvars = $this->db_read($key); - } - - if ($oldvars !== null) { - $newvars = $this->_fixvars($vars, $oldvars); - - 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); - } - } - else { - $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; - } - - /** * Merge vars with old vars and apply unsets */ - private function _fixvars($vars, $oldvars) + protected function _fixvars($vars, $oldvars) { if ($oldvars !== null) { $a_oldvars = $this->unserialize($oldvars); @@ -280,97 +168,6 @@ class rcube_session return $newvars; } - - /** - * Handler for session_destroy() - * - * @param string Session ID - * - * @return boolean True on success - */ - public function db_destroy($key) - { - if ($key) { - $this->db->query("DELETE FROM {$this->table_name} WHERE `sess_id` = ?", $key); - } - - return true; - } - - - /** - * Read session data from memcache - * - * @param string Session ID - * @return string Session vars - */ - public function mc_read($key) - { - if ($value = $this->memcache->get($key)) { - $arr = unserialize($value); - $this->changed = $arr['changed']; - $this->ip = $arr['ip']; - $this->vars = $arr['vars']; - $this->key = $key; - - return !empty($this->vars) ? (string) $this->vars : ''; - } - - return null; - } - - - /** - * Save session data. - * handler for session_read() - * - * @param string Session ID - * @param string Serialized session vars - * - * @return boolean True on success - */ - public function mc_write($key, $vars) - { - $ts = microtime(true); - - // no session data in cache (mc_read() returns false) - if (!$this->key) - $oldvars = null; - // use internal data for fast requests (up to 0.5 sec.) - else if ($key == $this->key && (!$this->vars || $ts - $this->start < 0.5)) - $oldvars = $this->vars; - else // else read data again - $oldvars = $this->mc_read($key); - - $newvars = $oldvars !== null ? $this->_fixvars($vars, $oldvars) : $vars; - - if ($newvars !== $oldvars || $ts - $this->changed > $this->lifetime / 3) { - return $this->memcache->set($key, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $newvars)), - MEMCACHE_COMPRESSED, $this->lifetime + 60); - } - - return true; - } - - - /** - * Handler for session_destroy() with memcache backend - * - * @param string Session ID - * - * @return boolean True on success - */ - public function mc_destroy($key) - { - if ($key) { - // #1488592: use 2nd argument - $this->memcache->delete($key, 0); - } - - return true; - } - - /** * Execute registered garbage collector routines */ @@ -381,7 +178,6 @@ class rcube_session return $this->gc_enabled = $maxlifetime; } - /** * Register additional garbage collector functions * @@ -422,6 +218,7 @@ class rcube_session * Generate and set new session id * * @param boolean $destroy If enabled the current session will be destroyed + * @return bool */ public function regenerate_id($destroy=true) { @@ -433,6 +230,28 @@ class rcube_session return true; } + /** + * see if we have vars of this key already cached, and if so, return them. + * + * @param $key + * @return null|array + */ + protected function get_cache($key) + { + // no session data in cache (read() returns false) + if (!$this->key) { + $cache = null; + } + // use internal data for fast requests (up to 0.5 sec.) + else if ($key == $this->key && (!$this->vars || $ts - $this->start < 0.5)) { + $cache = $this->vars; + } + else { // else read data again + $cache = $this->read($key); + } + return $cache; + } + /** * Append the given value to the certain node in the session data array @@ -523,10 +342,9 @@ class rcube_session $node[$k] = $value; } - if ($this->key && $this->memcache) - $data = $this->mc_read($this->key); - else if ($this->key) - $data = $this->db_read($this->key); + if($this->key) { + $data = $this->read($this->key); + } if ($data) { session_decode($data); @@ -553,7 +371,7 @@ class rcube_session * Returns a reference to the node in data array referenced by the given path. * e.g. ['compose','attachments'] will return $_SESSION['compose']['attachments'] */ - private function &get_node($path, &$data_arr) + protected function &get_node($path, &$data_arr) { $node = &$data_arr; if (!empty($path)) { @@ -570,7 +388,7 @@ class rcube_session /** * Serialize session data */ - private function serialize($vars) + protected function serialize($vars) { $data = ''; if (is_array($vars)) { @@ -589,7 +407,7 @@ class rcube_session * Unserialize session data * http://www.php.net/manual/en/function.session-decode.php#56106 */ - private function unserialize($str) + protected function unserialize($str) { $str = (string)$str; $endptr = strlen($str); @@ -788,6 +606,7 @@ class rcube_session * Create session cookie from session data * * @param int Time slot to use + * @return string */ function _mkcookie($timeslot) { 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 diff --git a/program/lib/Roundcube/rcube_session_memcache.php b/program/lib/Roundcube/rcube_session_memcache.php new file mode 100644 index 000000000..85a4aa617 --- /dev/null +++ b/program/lib/Roundcube/rcube_session_memcache.php @@ -0,0 +1,140 @@ + | + | Author: Aleksander Machniak | + | Author: Cor Bosman | + +-----------------------------------------------------------------------+ +*/ + +/** + * Class to provide memcache session storage + * + * @package Framework + * @subpackage Core + * @author Thomas Bruederli + * @author Aleksander Machniak + * @author Cor Bosman + */ +class rcube_session_memcache extends rcube_session +{ + private $memcache; + + public function __construct() + { + $this->memcache = rcube::get_instance()->get_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"), + true, true); + } + + // register sessions handler + $this->register_session_handler(); + + } + + /** + * @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() with memcache backend + * + * @param $key + * @return bool + */ + public function destroy($key) + { + if ($key) { + // #1488592: use 2nd argument + $this->memcache->delete($key, 0); + } + + return true; + } + + + /** + * Read session data from memcache + * + * @param $key + * @return null|string + */ + public function read($key) + { + if ($value = $this->memcache->get($key)) { + $arr = unserialize($value); + $this->changed = $arr['changed']; + $this->ip = $arr['ip']; + $this->vars = $arr['vars']; + $this->key = $key; + + return !empty($this->vars) ? (string) $this->vars : ''; + } + + return null; + } + + /** + * write data to memcache storage + * + * @param $key + * @param $vars + * @return bool + */ + public function write($key, $vars) + { + return $this->memcache->set($key, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $vars)), + MEMCACHE_COMPRESSED, $this->lifetime + 60); + } + + /** + * update memcache session data + * + * @param $key + * @param $newvars + * @param $oldvars + * @return bool + */ + public function update($key, $newvars, $oldvars) + { + $ts = microtime(true); + + if ($newvars !== $oldvars || $ts - $this->changed > $this->lifetime / 3) { + return $this->memcache->set($key, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $newvars)), + MEMCACHE_COMPRESSED, $this->lifetime + 60); + } + + return true; + } + +} \ No newline at end of file diff --git a/program/lib/Roundcube/rcube_session_php.php b/program/lib/Roundcube/rcube_session_php.php new file mode 100644 index 000000000..73a889259 --- /dev/null +++ b/program/lib/Roundcube/rcube_session_php.php @@ -0,0 +1,71 @@ + | + | Author: Aleksander Machniak | + | Author: Cor Bosman | + +-----------------------------------------------------------------------+ +*/ + +/** + * Class to provide native php session storage + * + * @package Framework + * @subpackage Core + * @author Thomas Bruederli + * @author Aleksander Machniak + * @author Cor Bosman + */ +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. + */ + + public function open($save_path, $session_name) {} + public function close() {} + public function destroy($key) {} + public function read($key) {} + public function write($key, $vars) {} + public function update($key, $newvars, $oldvars) {} + + + /** + * Wrapper for session_write_close() + */ + public function write_close() + { + $_SESSION['__IP'] = $this->ip; + $_SESSION['__MTIME'] = time(); + + parent::write_close(); + } + + /** + * Wrapper for session_start() + */ + public function start($config) + { + parent::start($config); + + $this->key = session_id(); + $this->ip = $_SESSION['__IP']; + $this->changed = $_SESSION['__MTIME']; + + } + +} \ No newline at end of file diff --git a/program/lib/Roundcube/rcube_session_redis.php b/program/lib/Roundcube/rcube_session_redis.php new file mode 100644 index 000000000..07a91cc45 --- /dev/null +++ b/program/lib/Roundcube/rcube_session_redis.php @@ -0,0 +1,210 @@ + | + | Author: Aleksander Machniak | + | Author: Cor Bosman | + +-----------------------------------------------------------------------+ +*/ + +/** + * Class to provide redis session storage + * + * @package Framework + * @subpackage Core + * @author Cor Bosman + */ +class rcube_session_redis extends rcube_session { + + private $redis; + + public function __construct() + { + // instantiate Redis object + $this->redis = new 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"), + true, true); + } + + // get config instance + $hosts = rcube::get_instance()->config->get('redis_hosts', array()); + + // host config is wrong + if (!is_array($hosts) || empty($hosts) ) { + rcube::raise_error(array('code' => 604, 'type' => 'session', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Redis host not configured"), + true, true); + } + + // only allow 1 host for now until we support clustering + if (count($hosts) > 1) { + rcube::raise_error(array('code' => 604, 'type' => 'session', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Redis cluster not yet supported"), + true, true); + } + + foreach($hosts as $config) { + // explode individual fields + list($host, $port, $database, $password) = array_pad(explode(':', $config, 4), 4, null); + + // set default values if not set + $host = ($host !== null) ? $host : '127.0.0.1'; + $port = ($port !== null) ? $port : 6379; + $database = ($database !== null) ? $database : 0; + + if ($this->redis->connect($host, $port) === false) { + rcube::raise_error( + array( + 'code' => 604, + 'type' => 'session', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Could not connect to Redis server. Please check host and port" + ), + true, + true + ); + } + + if ($password != null && $this->redis->auth($password) === false) { + rcube::raise_error( + array( + 'code' => 604, + 'type' => 'session', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Could not authenticate with Redis server. Please check password." + ), + true, + true + ); + } + + if ($database != 0 && $this->redis->select($database) === false) { + rcube::raise_error( + array( + 'code' => 604, + 'type' => 'session', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Could not select Redis database. Please check database setting." + ), + true, + true + ); + } + } + + // register sessions handler + $this->register_session_handler(); + + } + + /** + * @param $save_path + * @param $session_name + * @return bool + */ + public function open($save_path, $session_name) + { + return true; + } + + /** + * @return bool + */ + public function close() + { + return true; + } + + /** + * remove data from store + * + * @param $key + * @return bool + */ + public function destroy($key) + { + if ($key) { + $this->redis->del($key); + } + + return true; + } + + + /** + * read data from redis store + * + * @param $key + * @return null + */ + public function read($key) + { + if ($value = $this->redis->get($key)) { + $arr = unserialize($value); + $this->changed = $arr['changed']; + $this->ip = $arr['ip']; + $this->vars = $arr['vars']; + $this->key = $key; + + return !empty($this->vars) ? (string) $this->vars : ''; + } + + return null; + } + + + /** + * write data to redis store + * + * @param $key + * @param $newvars + * @param $oldvars + * @return bool + */ + public function update($key, $newvars, $oldvars) + { + $ts = microtime(true); + + if ($newvars !== $oldvars || $ts - $this->changed > $this->lifetime / 3) { + $this->redis->setex($key, $this->lifetime + 60, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $newvars))); + } + + return true; + } + + + /** + * write data to redis store + * + * @param $key + * @param $vars + * @return bool + */ + public function write($key, $vars) + { + return $this->redis->setex($key, $this->lifetime + 60, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $vars))); + } + + +} \ No newline at end of file -- cgit v1.2.3 From bd82526e41e54fa001c851d48b4e7823aa5e7bcd Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 22 Feb 2015 10:43:48 +0100 Subject: Fix missing or not up-to-date CATEGORIES entry in vCard export (#1490277) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_contacts.php | 5 +++++ program/lib/Roundcube/rcube_vcard.php | 4 ++++ program/steps/addressbook/export.inc | 25 +++++++++++++++---------- 4 files changed, 25 insertions(+), 10 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 25f7e19c4..38cf044dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ CHANGELOG Roundcube Webmail - Fix needless security warning on BMP attachments display (#1490282) - Fix handling of some improper constructs in format=flowed text as per the RFC3676[4.5] (#1490284) - Fix performance of rcube_db_mysql::get_variable() +- Fix missing or not up-to-date CATEGORIES entry in vCard export (#1490277) RELEASE 1.1.0 ------------- diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 6ac9fd5de..475f15696 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -714,6 +714,11 @@ class rcube_contacts extends rcube_addressbook // copy values into vcard object $vcard = new rcube_vcard($record['vcard'] ? $record['vcard'] : $save_data['vcard'], RCUBE_CHARSET, false, $this->vcard_fieldmap); $vcard->reset(); + + // don't store groups in vCard (#1490277) + $vcard->set('groups', null); + unset($save_data['groups']); + foreach ($save_data as $key => $values) { list($field, $section) = explode(':', $key); $fulltext = in_array($field, $this->fulltext_cols); diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php index 7f6b11851..c0e261df4 100644 --- a/program/lib/Roundcube/rcube_vcard.php +++ b/program/lib/Roundcube/rcube_vcard.php @@ -393,6 +393,10 @@ class rcube_vcard $this->raw[$tag][$index]['type'] = explode(',', ($typemap[$type_uc] ? $typemap[$type_uc] : $type)); } } + else { + unset($this->raw[$tag]); + } + break; } } diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc index c2f22cbe2..9fbdb9663 100644 --- a/program/steps/addressbook/export.inc +++ b/program/steps/addressbook/export.inc @@ -121,14 +121,11 @@ exit; */ function prepare_for_export(&$record, $source = null) { - $groups = $source && $source->groups && $source->export_groups ? $source->get_record_groups($record['ID']) : null; + $groups = $source && $source->groups && $source->export_groups ? $source->get_record_groups($record['ID']) : null; + $fieldmap = $source ? $source->vcard_map : null; if (empty($record['vcard'])) { - $vcard = new rcube_vcard(); - if ($source) { - $vcard->extend_fieldmap($source->vcard_map); - } - $vcard->load($record['vcard']); + $vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap); $vcard->reset(); foreach ($record as $key => $values) { @@ -151,11 +148,19 @@ function prepare_for_export(&$record, $source = null) $vcard->set('groups', join(',', $groups), null); } - $record['vcard'] = $vcard->export(true); + $record['vcard'] = $vcard->export(); } // patch categories to alread existing vcard block - else if ($record['vcard'] && !empty($groups) && !strpos($record['vcard'], 'CATEGORIES:')) { - $vgroups = 'CATEGORIES:' . rcube_vcard::vcard_quote(join(',', $groups)); - $record['vcard'] = str_replace('END:VCARD', $vgroups . rcube_vcard::$eol . 'END:VCARD', $record['vcard']); + else if ($record['vcard']) { + $vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap); + + // unset CATEGORIES entry, it might be not up-to-date (#1490277) + $vcard->set('groups', null); + $record['vcard'] = $vcard->export(); + + if (!empty($groups)) { + $vgroups = 'CATEGORIES:' . rcube_vcard::vcard_quote($groups, ','); + $record['vcard'] = str_replace('END:VCARD', $vgroups . rcube_vcard::$eol . 'END:VCARD', $record['vcard']); + } } } -- cgit v1.2.3 From f070da7c27e151353efd6dd7a639418991f73c7c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 22 Feb 2015 11:47:14 +0100 Subject: Fix fatal errors on systems without mbstring extension or mb_regex_encoding() function (#1490280) --- CHANGELOG | 1 + program/include/iniset.php | 11 +++++++---- program/lib/Roundcube/bootstrap.php | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 38cf044dd..92b8016db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ CHANGELOG Roundcube Webmail - Fix handling of some improper constructs in format=flowed text as per the RFC3676[4.5] (#1490284) - Fix performance of rcube_db_mysql::get_variable() - Fix missing or not up-to-date CATEGORIES entry in vCard export (#1490277) +- Fix fatal errors on systems without mbstring extension or mb_regex_encoding() function (#1490280) RELEASE 1.1.0 ------------- diff --git a/program/include/iniset.php b/program/include/iniset.php index ca1e6ad75..d91b31436 100644 --- a/program/include/iniset.php +++ b/program/include/iniset.php @@ -68,11 +68,14 @@ spl_autoload_register('rcmail_autoload'); // backward compatybility (to be removed) require_once INSTALL_PATH . 'program/include/bc.php'; -// load the UTF-8 portablity layer from Patchwork -if (!function_exists('iconv') || !function_exists('utf8_encode') || !extension_loaded('mbstring')) { - \Patchwork\Utf8\Bootup::initAll(); +// load the UTF-8 portability layers from Patchwork +// don't load mbstring layer as it conflicts with Roundcube Framework (#1490280) +if (!function_exists('iconv')) { + \Patchwork\Utf8\Bootup::initIconv(); +} +if (!function_exists('utf8_encode')) { + \Patchwork\Utf8\Bootup::initUtf8Encode(); } - /** * PHP5 autoloader routine for dynamic class loading diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php index 24c1f86d4..0c950dc14 100644 --- a/program/lib/Roundcube/bootstrap.php +++ b/program/lib/Roundcube/bootstrap.php @@ -78,9 +78,11 @@ if (!defined('RCUBE_LOCALIZATION_DIR')) { } // set internal encoding for mbstring extension -if (extension_loaded('mbstring')) { +if (function_exists('mb_internal_encoding')) { mb_internal_encoding(RCUBE_CHARSET); - @mb_regex_encoding(RCUBE_CHARSET); +} +if (function_exists('mb_regex_encoding')) { + mb_regex_encoding(RCUBE_CHARSET); } // make sure the Roundcube lib directory is in the include_path -- cgit v1.2.3 From 6c8cbe3ee5f0b215e8b5211127c01188d7bec030 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 23 Feb 2015 11:06:55 -0500 Subject: Fix regression where require_plugin() wasn't initializing the plugin --- program/lib/Roundcube/rcube_plugin_api.php | 91 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 45 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 8fd3253e0..1a10b8de2 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -43,6 +43,7 @@ class rcube_plugin_api public $active_plugins = array(); protected $plugins = array(); + protected $plugins_initialized = array(); protected $tasks = array(); protected $actions = array(); protected $actionmap = array(); @@ -96,6 +97,7 @@ class rcube_plugin_api // ... task, request type and framed mode if (!$this->filter($plugin)) { $plugin->init(); + $this->plugins_initialized[$plugin->ID] = $plugin; } } @@ -130,7 +132,7 @@ class rcube_plugin_api // load required core plugin if no derivate was found if (!$loaded) { - $loaded = $this->load_plugin($plugin_name); + $loaded = $this->load_plugin($plugin_name, true); } // trigger fatal error if still not loaded @@ -146,7 +148,7 @@ class rcube_plugin_api /** * Load the specified plugin * - * @param string Plugin name + * @param string Plugin name * @param boolean Force loading of the plugin even if it doesn't match the filter * @param boolean Require loading of the plugin, error if it doesn't exist * @@ -161,63 +163,62 @@ class rcube_plugin_api $plugins_dir = unslashify($dir->path); } - // plugin already loaded - if ($this->plugins[$plugin_name]) { - return true; - } + // plugin already loaded? + if (!$this->plugins[$plugin_name]) { + $fn = "$plugins_dir/$plugin_name/$plugin_name.php"; + + if (!is_readable($fn)) { + if ($require) { + rcube::raise_error(array('code' => 520, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Failed to load plugin file $fn"), true, false); + } - $fn = "$plugins_dir/$plugin_name/$plugin_name.php"; + return false; + } - if (is_readable($fn)) { if (!class_exists($plugin_name, false)) { include $fn; } // instantiate class if exists - if (class_exists($plugin_name, false)) { - $plugin = new $plugin_name($this); - $this->active_plugins[] = $plugin_name; - - // check inheritance... - if (is_subclass_of($plugin, 'rcube_plugin')) { - // ... task, request type and framed mode - - // call onload method on plugin if it exists. - // this is useful if you want to be called early in the boot process - if (method_exists($plugin, 'onload')) { - $plugin->onload(); - } + if (!class_exists($plugin_name, false)) { + rcube::raise_error(array('code' => 520, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "No plugin class $plugin_name found in $fn"), + true, false); - // init a plugin only if $force is set or if we're called after initialization - if (($force || $this->initialized) - && !$this->filter($plugin)) - { - $plugin->init(); - } + return false; + } - $this->plugins[$plugin_name] = $plugin; + $plugin = new $plugin_name($this); + $this->active_plugins[] = $plugin_name; - if (!empty($plugin->allowed_prefs)) { - $this->allowed_prefs = array_merge($this->allowed_prefs, $plugin->allowed_prefs); - } + // check inheritance... + if (is_subclass_of($plugin, 'rcube_plugin')) { + // call onload method on plugin if it exists. + // this is useful if you want to be called early in the boot process + if (method_exists($plugin, 'onload')) { + $plugin->onload(); + } - return true; + if (!empty($plugin->allowed_prefs)) { + $this->allowed_prefs = array_merge($this->allowed_prefs, $plugin->allowed_prefs); } - } - else { - rcube::raise_error(array('code' => 520, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "No plugin class $plugin_name found in $fn"), - true, false); + + $this->plugins[$plugin_name] = $plugin; } } - else if ($require) { - rcube::raise_error(array('code' => 520, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Failed to load plugin file $fn"), true, false); + + if ($plugin = $this->plugins[$plugin_name]) { + // init a plugin only if $force is set or if we're called after initialization + if (($force || $this->initialized) && !$this->plugins_initialized[$plugin_name] && !$this->filter($plugin)) { + $plugin->init(); + $this->plugins_initialized[$plugin_name] = $plugin; + } } - return false; + return true; } /** @@ -228,9 +229,9 @@ class rcube_plugin_api */ private function filter($plugin) { - return (($plugin->noajax && !(is_object($this->output) && $this->output->type == 'html') ) + return ($plugin->noajax && !(is_object($this->output) && $this->output->type == 'html')) || ($plugin->task && !preg_match('/^('.$plugin->task.')$/i', $this->task)) - || ($plugin->noframe && !empty($_REQUEST['_framed']))) ? true : false; + || ($plugin->noframe && !empty($_REQUEST['_framed'])); } /** -- cgit v1.2.3 From 4d90e6596dfd7b7e2d0a3155667632bf0f42750b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 24 Feb 2015 05:15:35 -0500 Subject: Use also Organization field as a fallback if contact has no name(s) specified --- program/lib/Roundcube/rcube_addressbook.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php index 69027b0e8..69f8bf934 100644 --- a/program/lib/Roundcube/rcube_addressbook.php +++ b/program/lib/Roundcube/rcube_addressbook.php @@ -544,13 +544,20 @@ abstract class rcube_addressbook $fn = trim($fn, ', '); - // fallback to display name - if (empty($fn) && $contact['name']) - $fn = $contact['name']; - - // fallback to email address - if (empty($fn) && ($email = self::get_col_values('email', $contact, true)) && !empty($email)) { - return $email[0]; + // fallbacks... + if ($fn === '') { + // ... display name + if (!empty($contact['name'])) { + $fn = $contact['name']; + } + // ... organization + else if (!empty($contact['organization'])) { + $fn = $contact['organization']; + } + // ... email address + else if (($email = self::get_col_values('email', $contact, true)) && !empty($email)) { + $fn = $email[0]; + } } return $fn; -- cgit v1.2.3 From 75e8a7d31352770ed029286e1e1b7bdb64311da9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 24 Feb 2015 11:38:22 +0100 Subject: Really make sure plugins aren't initialized twice --- program/lib/Roundcube/rcube_plugin_api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 1a10b8de2..9bc60f639 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -95,7 +95,7 @@ class rcube_plugin_api foreach ($this->plugins as $plugin) { // ... task, request type and framed mode - if (!$this->filter($plugin)) { + if (!$this->plugins_initialized[$plugin_name] && !$this->filter($plugin)) { $plugin->init(); $this->plugins_initialized[$plugin->ID] = $plugin; } @@ -132,7 +132,7 @@ class rcube_plugin_api // load required core plugin if no derivate was found if (!$loaded) { - $loaded = $this->load_plugin($plugin_name, true); + $loaded = $this->load_plugin($plugin_name); } // trigger fatal error if still not loaded -- cgit v1.2.3 From 62b847bea39b00c4795d10443bf5105d49ff7e07 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 24 Feb 2015 11:53:55 +0100 Subject: Fix "PHP Fatal error: Call to a member function getMessage() on a non-object" --- program/lib/Roundcube/rcube_smtp.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_smtp.php b/program/lib/Roundcube/rcube_smtp.php index c3a51467b..b37b44426 100644 --- a/program/lib/Roundcube/rcube_smtp.php +++ b/program/lib/Roundcube/rcube_smtp.php @@ -288,7 +288,8 @@ class rcube_smtp } // Send the message's headers and the body as SMTP data. - if (PEAR::isError($this->conn->data($data, $text_headers))) { + $result = $this->conn->data($data, $text_headers); + if (PEAR::isError($result)) { $err = $this->conn->getResponse(); if (!in_array($err[0], array(354, 250, 221))) { $msg = sprintf('[%d] %s', $err[0], $err[1]); -- cgit v1.2.3 From 8f485469c7955fbf5b420ee0b6f043282965715b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 24 Feb 2015 12:23:11 +0100 Subject: Add possibility to configure max_allowed_packet value for all database engines (#1490283) --- CHANGELOG | 1 + config/defaults.inc.php | 6 ++++++ program/lib/Roundcube/rcube_db.php | 2 +- program/lib/Roundcube/rcube_db_mysql.php | 6 ++++++ program/lib/Roundcube/rcube_db_pgsql.php | 4 +++- 5 files changed, 17 insertions(+), 2 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index ee4a81e7d..064023187 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ CHANGELOG Roundcube Webmail - Plugin API: Add special onload() method to execute plugin actions before startup (session and GUI initialization) - Add possibility to print contact information (of a single contact) +- Add possibility to configure max_allowed_packet value for all database engines (#1490283) - Fix refreshing of drafts list when sending a message which was saved in meantime (#1490238) - Fix saving/sending emoticon images when assets_dir is set - Fix PHP fatal error when visiting Vacation interface and there's no sieve script yet diff --git a/config/defaults.inc.php b/config/defaults.inc.php index 06ea9ec21..edc5b32df 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -51,6 +51,12 @@ $config['db_table_dsn'] = array( // 'cache_messages' => 'r', ); +// It is possible to specify database variable values e.g. some limits here. +// Use them if your server is not MySQL or for better performance. +// For example Roundcube uses max_allowed_packet value (in bytes) +// which limits query size for database cache operations. +$config['db_max_allowed_packet'] = 23423440; + // ---------------------------------- // LOGGING/DEBUGGING diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index ab7058f2f..2cacb3013 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -357,7 +357,7 @@ class rcube_db public function get_variable($varname, $default = null) { // to be implemented by driver class - return $default; + return rcube::get_instance()->config->get('db_' . $varname, $default); } /** diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index 067e94be6..dd28c25c8 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -167,6 +167,12 @@ class rcube_db_mysql extends rcube_db return $this->variables[$varname]; } + // configured value has higher prio + $conf_value = rcube::get_instance()->config->get('db_' . $varname); + if ($conf_value !== null) { + return $this->variables[$varname] = $conf_value; + } + $result = $this->query('SHOW VARIABLES LIKE ?', $varname); while ($row = $this->fetch_array($result)) { diff --git a/program/lib/Roundcube/rcube_db_pgsql.php b/program/lib/Roundcube/rcube_db_pgsql.php index d33cdd859..ff41df224 100644 --- a/program/lib/Roundcube/rcube_db_pgsql.php +++ b/program/lib/Roundcube/rcube_db_pgsql.php @@ -139,9 +139,11 @@ class rcube_db_pgsql extends rcube_db // There's a known case when max_allowed_packet is queried // PostgreSQL doesn't have such limit, return immediately if ($varname == 'max_allowed_packet') { - return $default; + return rcube::get_instance()->config->get('db_' . $varname, $default); } + $this->variables[$varname] = rcube::get_instance()->config->get('db_' . $varname); + if (!isset($this->variables)) { $this->variables = array(); -- cgit v1.2.3 From 83f1f6b12fb5405ca598322e09b3ccd0842ccc85 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 25 Feb 2015 09:22:54 -0500 Subject: Improve compose_search_name() to not return "email@address " entries which may happen for contacts without name(s) --- program/lib/Roundcube/rcube_addressbook.php | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php index 69f8bf934..31189e0fc 100644 --- a/program/lib/Roundcube/rcube_addressbook.php +++ b/program/lib/Roundcube/rcube_addressbook.php @@ -594,6 +594,13 @@ abstract class rcube_addressbook switch ($key) { case 'name': $value = $name ?: self::compose_list_name($contact); + + // If name(s) are undefined compose_list_name() may return an email address + // here we prevent from returning the same name and email + if ($name === $email && strpos($result, '{email}') !== false) { + $value = ''; + } + break; case 'email': -- cgit v1.2.3 From 2a31f6dbd7c63232918d175fb2879682217946ea Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 25 Feb 2015 21:08:16 +0100 Subject: Reset default db_max_allowed_packet, fix max packet size detection --- config/defaults.inc.php | 2 +- program/lib/Roundcube/rcube_cache.php | 6 ++++-- program/lib/Roundcube/rcube_cache_shared.php | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/config/defaults.inc.php b/config/defaults.inc.php index edc5b32df..50c392db3 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -55,7 +55,7 @@ $config['db_table_dsn'] = array( // Use them if your server is not MySQL or for better performance. // For example Roundcube uses max_allowed_packet value (in bytes) // which limits query size for database cache operations. -$config['db_max_allowed_packet'] = 23423440; +$config['db_max_allowed_packet'] = null; // ---------------------------------- diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php index 52a2db997..303abdac4 100644 --- a/program/lib/Roundcube/rcube_cache.php +++ b/program/lib/Roundcube/rcube_cache.php @@ -605,8 +605,10 @@ class rcube_cache $this->max_packet = 2097152; // default/max is 2 MB if ($this->type == 'db') { - $value = $this->db->get_variable('max_allowed_packet', $this->max_packet); - $this->max_packet = max($value, $this->max_packet) - 2000; + if ($value = $this->db->get_variable('max_allowed_packet', $this->max_packet)) { + $this->max_packet = $value; + } + $this->max_packet -= 2000; } else if ($this->type == 'memcache') { $stats = $this->db->getStats(); diff --git a/program/lib/Roundcube/rcube_cache_shared.php b/program/lib/Roundcube/rcube_cache_shared.php index 339a9aa20..3f0f20e41 100644 --- a/program/lib/Roundcube/rcube_cache_shared.php +++ b/program/lib/Roundcube/rcube_cache_shared.php @@ -595,8 +595,10 @@ class rcube_cache_shared $this->max_packet = 2097152; // default/max is 2 MB if ($this->type == 'db') { - $value = $this->db->get_variable('max_allowed_packet', 1048500); - $this->max_packet = min($value, $this->max_packet) - 2000; + if ($value = $this->db->get_variable('max_allowed_packet', $this->max_packet)) { + $this->max_packet = $value; + } + $this->max_packet -= 2000; } else if ($this->type == 'memcache') { $stats = $this->db->getStats(); -- cgit v1.2.3 From b59b72cc3028cc0514e951f135d8bfe7efcaaa6f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 26 Feb 2015 18:04:03 +0100 Subject: Fix "Non-static method PEAR::isError() should not be called statically" errors (#1490281) --- CHANGELOG | 1 + plugins/managesieve/lib/Roundcube/rcube_sieve.php | 93 +++++++++++++++------- .../lib/Roundcube/rcube_sieve_engine.php | 5 +- plugins/password/drivers/ldap.php | 6 +- plugins/password/drivers/poppassd.php | 2 +- plugins/password/drivers/vpopmaild.php | 9 ++- program/lib/Roundcube/rcube.php | 15 ++-- program/lib/Roundcube/rcube_smtp.php | 14 ++-- program/steps/mail/sendmail.inc | 7 +- 9 files changed, 99 insertions(+), 53 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 72ccf7043..4855f2c31 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ CHANGELOG Roundcube Webmail - Fix cursor position on reply below the quote in HTML mode (#1490263) - Fix so "over quota" errors are displayed also in message compose page - Fix duplicate entries supression in autocomplete result (#1490290) +- Fix "Non-static method PEAR::isError() should not be called statically" errors (#1490281) RELEASE 1.1.0 ------------- diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve.php b/plugins/managesieve/lib/Roundcube/rcube_sieve.php index 389c85012..59a7bc134 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve.php @@ -68,7 +68,9 @@ class rcube_sieve $this->sieve->setDebug(true, array($this, 'debug_handler')); } - if (PEAR::isError($this->sieve->connect($host, $port, $options, $usetls))) { + $result = $this->sieve->connect($host, $port, $options, $usetls); + + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_CONNECTION); } @@ -78,9 +80,9 @@ class rcube_sieve $password = $auth_pw; } - if (PEAR::isError($this->sieve->login($username, $password, - $auth_type ? strtoupper($auth_type) : null, $authz)) - ) { + $result = $this->sieve->login($username, $password, $auth_type ? strtoupper($auth_type) : null, $authz); + + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_LOGIN); } @@ -115,22 +117,28 @@ class rcube_sieve */ public function save($name = null) { - if (!$this->sieve) + if (!$this->sieve) { return $this->_set_error(self::ERROR_INTERNAL); + } - if (!$this->script) + if (!$this->script) { return $this->_set_error(self::ERROR_INTERNAL); + } - if (!$name) + if (!$name) { $name = $this->current; + } $script = $this->script->as_text(); - if (!$script) + if (!$script) { $script = '/* empty script */'; + } - if (PEAR::isError($this->sieve->installScript($name, $script))) + $result = $this->sieve->installScript($name, $script); + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_INSTALL); + } return true; } @@ -140,14 +148,19 @@ class rcube_sieve */ public function save_script($name, $content = null) { - if (!$this->sieve) + if (!$this->sieve) { return $this->_set_error(self::ERROR_INTERNAL); + } - if (!$content) + if (!$content) { $content = '/* empty script */'; + } + + $result = $this->sieve->installScript($name, $content); - if (PEAR::isError($this->sieve->installScript($name, $content))) + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_INSTALL); + } return true; } @@ -157,14 +170,19 @@ class rcube_sieve */ public function activate($name = null) { - if (!$this->sieve) + if (!$this->sieve) { return $this->_set_error(self::ERROR_INTERNAL); + } - if (!$name) + if (!$name) { $name = $this->current; + } - if (PEAR::isError($this->sieve->setActive($name))) + $result = $this->sieve->setActive($name); + + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_ACTIVATE); + } return true; } @@ -174,11 +192,15 @@ class rcube_sieve */ public function deactivate() { - if (!$this->sieve) + if (!$this->sieve) { return $this->_set_error(self::ERROR_INTERNAL); + } + + $result = $this->sieve->setActive(''); - if (PEAR::isError($this->sieve->setActive(''))) + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_DEACTIVATE); + } return true; } @@ -188,22 +210,32 @@ class rcube_sieve */ public function remove($name = null) { - if (!$this->sieve) + if (!$this->sieve) { return $this->_set_error(self::ERROR_INTERNAL); + } - if (!$name) + if (!$name) { $name = $this->current; + } // script must be deactivated first - if ($name == $this->sieve->getActive()) - if (PEAR::isError($this->sieve->setActive(''))) + if ($name == $this->sieve->getActive()) { + $result = $this->sieve->setActive(''); + + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_DELETE); + } + } + + $result = $this->sieve->removeScript($name); - if (PEAR::isError($this->sieve->removeScript($name))) + if (is_a($result, 'PEAR_Error')) { return $this->_set_error(self::ERROR_DELETE); + } - if ($name == $this->current) + if ($name == $this->current) { $this->current = null; + } return true; } @@ -221,7 +253,7 @@ class rcube_sieve $ext = $this->sieve->getExtensions(); - if (PEAR::isError($ext)) { + if (is_a($ext, 'PEAR_Error')) { return array(); } @@ -250,8 +282,9 @@ class rcube_sieve $list = $this->sieve->listScripts(); - if (PEAR::isError($list)) + if (is_a($list, 'PEAR_Error')) { return $this->_set_error(self::ERROR_OTHER); + } $this->list = $list; } @@ -283,8 +316,9 @@ class rcube_sieve $script = $this->sieve->getScript($name); - if (PEAR::isError($script)) + if (is_a($script, 'PEAR_Error')) { return $this->_set_error(self::ERROR_OTHER); + } // try to parse from Roundcube format $this->script = $this->_parse($script); @@ -349,8 +383,9 @@ class rcube_sieve $content = $this->sieve->getScript($name); - if (PEAR::isError($content)) + if (is_a($content, 'PEAR_Error')) { return $this->_set_error(self::ERROR_OTHER); + } return $content; } @@ -366,10 +401,12 @@ class rcube_sieve if ($copy) { $content = $this->sieve->getScript($copy); - if (PEAR::isError($content)) + if (is_a($content, 'PEAR_Error')) { return $this->_set_error(self::ERROR_OTHER); + } } + return $this->save_script($name, $content); } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index 69ae4b8a6..98c4c952c 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -390,10 +390,11 @@ class rcube_sieve_engine } else if ($action == 'setget') { $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true); - $script = $this->sieve->get_script($script_name); + $script = $this->sieve->get_script($script_name); - if (PEAR::isError($script)) + if (is_a($script, 'PEAR_Error')) { exit; + } $browser = new rcube_browser; diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php index c18ff0f06..a11c38d17 100644 --- a/plugins/password/drivers/ldap.php +++ b/plugins/password/drivers/ldap.php @@ -75,7 +75,7 @@ class rcube_ldap_password $ldap = Net_LDAP2::connect($ldapConfig); // Checking for connection error - if (PEAR::isError($ldap)) { + if (is_a($ldap, 'PEAR_Error')) { return PASSWORD_CONNECT_ERROR; } @@ -176,7 +176,7 @@ class rcube_ldap_password $ldap = Net_LDAP2::connect($ldapConfig); - if (PEAR::isError($ldap)) { + if (is_a($ldap, 'PEAR_Error')) { return ''; } @@ -189,7 +189,7 @@ class rcube_ldap_password $result = $ldap->search($base, $filter, $options); $ldap->done(); - if (PEAR::isError($result) || ($result->count() != 1)) { + if (is_a($result, 'PEAR_Error') || ($result->count() != 1)) { return ''; } diff --git a/plugins/password/drivers/poppassd.php b/plugins/password/drivers/poppassd.php index 8ddbef5d3..7a2821083 100644 --- a/plugins/password/drivers/poppassd.php +++ b/plugins/password/drivers/poppassd.php @@ -42,7 +42,7 @@ class rcube_poppassd_password $poppassd = new Net_Socket(); $result = $poppassd->connect($rcmail->config->get('password_pop_host'), $rcmail->config->get('password_pop_port'), null); - if (PEAR::isError($result)) { + if (is_a($result, 'PEAR_Error')) { return $this->format_error_result(PASSWORD_CONNECT_ERROR, $result->getMessage()); } else { diff --git a/plugins/password/drivers/vpopmaild.php b/plugins/password/drivers/vpopmaild.php index a7644fc21..90fce02c5 100644 --- a/plugins/password/drivers/vpopmaild.php +++ b/plugins/password/drivers/vpopmaild.php @@ -28,12 +28,13 @@ class rcube_vpopmaild_password { function save($curpass, $passwd) { - $rcmail = rcmail::get_instance(); - // include('Net/Socket.php'); + $rcmail = rcmail::get_instance(); $vpopmaild = new Net_Socket(); + $host = $rcmail->config->get('password_vpopmaild_host'); + $port = $rcmail->config->get('password_vpopmaild_port'); - if (PEAR::isError($vpopmaild->connect($rcmail->config->get('password_vpopmaild_host'), - $rcmail->config->get('password_vpopmaild_port'), null))) { + $result = $vpopmaild->connect($hostname, $port, null); + if (is_a($result, 'PEAR_Error')) { return PASSWORD_CONNECT_ERROR; } diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 3aca88843..20f509e3d 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -1681,15 +1681,18 @@ class rcube if ($message->getParam('delay_file_io')) { // use common temp dir - $temp_dir = $this->config->get('temp_dir'); - $body_file = tempnam($temp_dir, 'rcmMsg'); - if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { + $temp_dir = $this->config->get('temp_dir'); + $body_file = tempnam($temp_dir, 'rcmMsg'); + $mime_result = $message->saveMessageBody($body_file); + + if (is_a($mime_result, 'PEAR_Error')) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$mime_result->getMessage()), - TRUE, FALSE); + true, false); return false; } + $msg_body = fopen($body_file, 'r'); } else { @@ -1732,11 +1735,11 @@ class rcube $msg_body = $message->get(); - if (PEAR::isError($msg_body)) { + if (is_a($msg_body, 'PEAR_Error')) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$msg_body->getMessage()), - TRUE, FALSE); + true, false); } else { $delim = $this->config->header_delimiter(); diff --git a/program/lib/Roundcube/rcube_smtp.php b/program/lib/Roundcube/rcube_smtp.php index b37b44426..0322a0d46 100644 --- a/program/lib/Roundcube/rcube_smtp.php +++ b/program/lib/Roundcube/rcube_smtp.php @@ -126,7 +126,7 @@ class rcube_smtp // try to connect to server and exit on failure $result = $this->conn->connect($CONFIG['smtp_timeout']); - if (PEAR::isError($result)) { + if (is_a($result, 'PEAR_Error')) { $this->response[] = "Connection failed: ".$result->getMessage(); $this->error = array('label' => 'smtpconnerror', 'vars' => array('code' => $this->conn->_code)); $this->conn = null; @@ -159,7 +159,7 @@ class rcube_smtp $result = $this->conn->auth($smtp_user, $smtp_pass, $smtp_auth_type, $use_tls, $smtp_authz); - if (PEAR::isError($result)) { + if (is_a($result, 'PEAR_Error')) { $this->error = array('label' => 'smtpautherror', 'vars' => array('code' => $this->conn->_code)); $this->response[] .= 'Authentication failure: ' . $result->getMessage() . ' (Code: ' . $result->getCode() . ')'; $this->reset(); @@ -240,7 +240,8 @@ class rcube_smtp } // set From: address - if (PEAR::isError($this->conn->mailFrom($from, $from_params))) { + $result = $this->conn->mailFrom($from, $from_params); + if (is_a($result, 'PEAR_Error')) { $err = $this->conn->getResponse(); $this->error = array('label' => 'smtpfromerror', 'vars' => array( 'from' => $from, 'code' => $err[0], 'msg' => $err[1])); @@ -252,7 +253,7 @@ class rcube_smtp // prepare list of recipients $recipients = $this->_parse_rfc822($recipients); - if (PEAR::isError($recipients)) { + if (is_a($recipients, 'PEAR_Error')) { $this->error = array('label' => 'smtprecipientserror'); $this->reset(); return false; @@ -260,7 +261,8 @@ class rcube_smtp // set mail recipients foreach ($recipients as $recipient) { - if (PEAR::isError($this->conn->rcptTo($recipient, $recipient_params))) { + $result = $this->conn->rcptTo($recipient, $recipient_params); + if (is_a($result, 'PEAR_Error')) { $err = $this->conn->getResponse(); $this->error = array('label' => 'smtptoerror', 'vars' => array( 'to' => $recipient, 'code' => $err[0], 'msg' => $err[1])); @@ -289,7 +291,7 @@ class rcube_smtp // Send the message's headers and the body as SMTP data. $result = $this->conn->data($data, $text_headers); - if (PEAR::isError($result)) { + if (is_a($result, 'PEAR_Error')) { $err = $this->conn->getResponse(); if (!in_array($err[0], array(354, 250, 221))) { $msg = sprintf('[%d] %s', $err[0], $err[1]); diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 08b085c88..9e674f773 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -601,8 +601,9 @@ if ($store_target) { else { $temp_dir = $RCMAIL->config->get('temp_dir'); $mailbody_file = tempnam($temp_dir, 'rcmMsg'); + $msg = $MAIL_MIME->saveMessageBody($mailbody_file); - if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file))) { + if (!is_a($msg, 'PEAR_Error')) { $msg = $mailbody_file; } } @@ -612,7 +613,7 @@ if ($store_target) { $headers = ''; } - if (PEAR::isError($msg)) { + if (is_a($msg, 'PEAR_Error')) { rcube::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$msg->getMessage()), @@ -800,7 +801,7 @@ function rcmail_fix_emoticon_paths($mime_message) if (!in_array($image_name, $included_images)) { // add the image to the MIME message $res = $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name); - if (PEAR::isError($res)) { + if (is_a($res, 'PEAR_Error')) { $RCMAIL->output->show_message("emoticonerror", 'error'); continue; } -- cgit v1.2.3 From 759566fe99fe1fcf2857afc51d0b343b67aa4d44 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 27 Feb 2015 09:13:39 +0100 Subject: Fix parsing invalid HTML messages with BOM after (#1490291) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_washtml.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 3bc673d51..b6b0e6511 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ CHANGELOG Roundcube Webmail - Fix so "over quota" errors are displayed also in message compose page - Fix duplicate entries supression in autocomplete result (#1490290) - Fix "Non-static method PEAR::isError() should not be called statically" errors (#1490281) +- Fix parsing invalid HTML messages with BOM after (#1490291) RELEASE 1.1.0 ------------- diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php index e0cce685b..b042f5f80 100644 --- a/program/lib/Roundcube/rcube_washtml.php +++ b/program/lib/Roundcube/rcube_washtml.php @@ -403,16 +403,23 @@ class rcube_washtml { // special replacements (not properly handled by washtml class) $html_search = array( - '/(<\/nobr>)(\s+)()/i', // space(s) between - '/]*>[^<]*<\/title>/i', // PHP bug #32547 workaround: remove title tag - '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/', // byte-order mark (only outlook?) - '/]+>/i', // washtml/DOMDocument cannot handle xml namespaces + // space(s) between + '/(<\/nobr>)(\s+)()/i', + // PHP bug #32547 workaround: remove title tag + '/]*>[^<]*<\/title>/i', + // remove before BOM (#1490291) + '/<\!doctype[^>]+>[^<]*/im', + // byte-order mark (only outlook?) + '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/', + // washtml/DOMDocument cannot handle xml namespaces + '/]+>/i', ); $html_replace = array( '\\1'.'   '.'\\3', '', '', + '', '', ); $html = preg_replace($html_search, $html_replace, trim($html)); -- cgit v1.2.3 From 5aa1d2005bb01f9f7f6bb2275dc467f7b7b368f4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 27 Feb 2015 10:06:30 +0100 Subject: Fix parsing ldap URI for cases where hostname is non-empty --- program/lib/Roundcube/rcube_ldap.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index 13d55bde6..87dcb2bf9 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -698,8 +698,9 @@ class rcube_ldap extends rcube_addressbook for ($i=0; $i < $entry['memberurl']['count']; $i++) { // extract components from url - if (!preg_match('!ldap:///([^\?]+)\?\?(\w+)\?(.*)$!', $entry['memberurl'][$i], $m)) + if (!preg_match('!ldap://[^/]*/([^\?]+)\?\?(\w+)\?(.*)$!', $entry['memberurl'][$i], $m)) { continue; + } // add search filter if any $filter = $this->filter ? '(&(' . $m[3] . ')(' . $this->filter . '))' : $m[3]; -- cgit v1.2.3 From a4ef49d6cf5814c2dabdaeb2397fb187769b6cb4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 27 Feb 2015 13:02:01 +0100 Subject: Fix duplicate entry on timezones list in rcube_config::timezone_name_from_abbr() (#1490293) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_config.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index cd43124da..871358f75 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ CHANGELOG Roundcube Webmail - Fix duplicate entries supression in autocomplete result (#1490290) - Fix "Non-static method PEAR::isError() should not be called statically" errors (#1490281) - Fix parsing invalid HTML messages with BOM after (#1490291) +- Fix duplicate entry on timezones list in rcube_config::timezone_name_from_abbr() (#1490293) RELEASE 1.1.0 ------------- diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php index 89e45449d..e80474af2 100644 --- a/program/lib/Roundcube/rcube_config.php +++ b/program/lib/Roundcube/rcube_config.php @@ -683,7 +683,6 @@ class rcube_config '180' => "Europe/Moscow", '210' => "Asia/Tehran", '240' => "Asia/Dubai", - '300' => "Asia/Karachi", '270' => "Asia/Kabul", '300' => "Asia/Karachi", '330' => "Asia/Kolkata", -- 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') 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 6e3d249655f7d93a5b48cabedd229304bb69df90 Mon Sep 17 00:00:00 2001 From: corbosman Date: Fri, 27 Feb 2015 16:36:26 +0100 Subject: remove commented out code --- program/lib/Roundcube/rcube.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index f15ae840a..3aa461e36 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -520,10 +520,7 @@ class rcube ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); - - // get storage driver from config - // $storage = $this->config->get('session_storage', 'db'); - + // get session driver instance $this->session = rcube_session::factory($this->config); -- cgit v1.2.3 From 83eeec6c0665ff18881ba05743b7368f9fb1c2f7 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 12:53:05 +0100 Subject: Add utility function to compose a full-text-like LDAP search filter --- program/lib/Roundcube/rcube_ldap_generic.php | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_ldap_generic.php b/program/lib/Roundcube/rcube_ldap_generic.php index a76ad6d06..40adbedbe 100644 --- a/program/lib/Roundcube/rcube_ldap_generic.php +++ b/program/lib/Roundcube/rcube_ldap_generic.php @@ -6,7 +6,7 @@ | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2006-2014, The Roundcube Dev Team | - | Copyright (C) 2012-2014, Kolab Systems AG | + | Copyright (C) 2012-2015, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -316,6 +316,46 @@ class rcube_ldap_generic extends Net_LDAP3 return $rec; } + + /** + * Compose an LDAP filter string matching all words from the search string + * in the given list of attributes. + * + * @param string $value Search value + * @param mixed $attrs List of LDAP attributes to search + * @param int $mode Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * @return string LDAP filter + */ + public static function fulltext_search_filter($value, $attributes, $mode = 1) + { + if (empty($attributes)) { + $attributes = array('cn'); + } + + $groups = array(); + $words = rcube_utils::tokenize_string($value, 1); + + // set wildcards + $wp = $ws = ''; + if ($mode != 1) { + $ws = '*'; + $wp = !$mode ? '*' : ''; + } + + // search each word in all listed attributes + foreach ($words as $word) { + $parts = array(); + foreach ($attributes as $attr) { + $parts[] = "($attr=$wp" . self::quote_string($word) . "$ws)"; + } + $groups[] = '(|' . join('', $parts) . ')'; + } + + return empty($groups) ? '' : '(&' . join('', $groups) . ')'; + } } // for backward compat. -- cgit v1.2.3 From 36ee2c8427298fc8735fe547d11c7e203fb3ca99 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 14:53:02 +0100 Subject: Improve LDAP search by ignoring words order in fuzzy substring matching mode --- program/lib/Roundcube/rcube_ldap.php | 66 +++++++++++++++++----------- program/lib/Roundcube/rcube_ldap_generic.php | 5 ++- 2 files changed, 44 insertions(+), 27 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index 87dcb2bf9..aad0090f8 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -792,33 +792,24 @@ class rcube_ldap extends rcube_addressbook return $this->result; } - // use AND operator for advanced searches - $filter = is_array($value) ? '(&' : '(|'; - // set wildcards - $wp = $ws = ''; - if (!empty($this->prop['fuzzy_search']) && $mode != 1) { - $ws = '*'; - if (!$mode) { - $wp = '*'; - } - } - - if ($fields == '*') { - // search_fields are required for fulltext search - if (empty($this->prop['search_fields'])) { - $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch'); - $this->result = new rcube_result_set(); - return $this->result; - } - if (is_array($this->prop['search_fields'])) { - foreach ($this->prop['search_fields'] as $field) { - $filter .= "($field=$wp" . rcube_ldap_generic::quote_string($value) . "$ws)"; + // advanced per-attribute search + if (is_array($value)) { + // use AND operator for advanced searches + $filter = '(&'; + + // set wildcards + $wp = $ws = ''; + if (!empty($this->prop['fuzzy_search']) && $mode != 1) { + $ws = '*'; + if (!$mode) { + $wp = '*'; } } - } - else { + foreach ((array)$fields as $idx => $field) { - $val = is_array($value) ? $value[$idx] : $value; + $val = $value[$idx]; + if (!strlen($val)) + continue; if ($attrs = $this->_map_field($field)) { if (count($attrs) > 1) $filter .= '(|'; @@ -828,8 +819,33 @@ class rcube_ldap extends rcube_addressbook $filter .= ')'; } } + + $filter .= ')'; + } + else { + if ($fields == '*') { + // search_fields are required for fulltext search + if (empty($this->prop['search_fields'])) { + $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch'); + $this->result = new rcube_result_set(); + return $this->result; + } + $attributes = (array)$this->prop['search_fields']; + } + else { + // map address book fields into ldap attributes + $me = $this; + $attributes = array(); + array_walk($fields, function($field) use ($me, &$attributes) { + if ($this->coltypes[$field] && ($attrs = (array)$this->coltypes[$field]['attributes'])) { + $attributes = array_merge($attributes, $attrs); + } + }); + } + + // compose a full-text-like search filter + $filter = rcube_ldap_generic::fulltext_search_filter($value, $attributes, $mode); } - $filter .= ')'; // add required (non empty) fields filter $req_filter = ''; diff --git a/program/lib/Roundcube/rcube_ldap_generic.php b/program/lib/Roundcube/rcube_ldap_generic.php index 40adbedbe..abe16760d 100644 --- a/program/lib/Roundcube/rcube_ldap_generic.php +++ b/program/lib/Roundcube/rcube_ldap_generic.php @@ -336,7 +336,8 @@ class rcube_ldap_generic extends Net_LDAP3 } $groups = array(); - $words = rcube_utils::tokenize_string($value, 1); + $value = str_replace('*', '', $value); + $words = $mode == 0 ? rcube_utils::tokenize_string($value, 1) : array($value); // set wildcards $wp = $ws = ''; @@ -354,7 +355,7 @@ class rcube_ldap_generic extends Net_LDAP3 $groups[] = '(|' . join('', $parts) . ')'; } - return empty($groups) ? '' : '(&' . join('', $groups) . ')'; + return count($groups) > 1 ? '(&' . join('', $groups) . ')' : join('', $groups); } } -- cgit v1.2.3 From c32998084d6d7995e7ef9a100a842b492f32b6f9 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 14:54:36 +0100 Subject: Add untility function to match strings ignoring word order --- program/lib/Roundcube/rcube_utils.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index f4c0e90ca..0ca2a9e31 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -917,7 +917,7 @@ class rcube_utils */ public static function tokenize_string($str, $minlen = 2) { - $expr = array('/[\s;\/+-]+/ui', '/(\d)[-.\s]+(\d)/u'); + $expr = array('/[\s;,"\'\/+-]+/ui', '/(\d)[-.\s]+(\d)/u'); $repl = array(' ', '\\1\\2'); if ($minlen > 1) { @@ -984,6 +984,28 @@ class rcube_utils return $as_array ? $arr : join(" ", $arr); } + /** + * Compare two strings for matching words (order not relevant) + * + * @param string Haystack + * @param string Needle + * @return boolen True if match, False otherwise + */ + public static function words_match($haystack, $needle) + { + $a_needle = self::tokenize_string($needle, 1); + $haystack = join(" ", self::tokenize_string($haystack, 1)); + + $hits = 0; + foreach ($a_needle as $w) { + if (stripos($haystack, $w) !== false) { + $hits++; + } + } + + return $hits >= count($a_needle); + } + /** * Parse commandline arguments into a hash array * -- cgit v1.2.3 From fd259bed691c35ff87211552c8b280db4fc1460a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 15:52:14 +0100 Subject: Adapt fulltext search in local address book to ignore words order --- program/lib/Roundcube/rcube_contacts.php | 80 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 41 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 475f15696..8ba3e6372 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -320,28 +320,8 @@ class rcube_contacts extends rcube_addressbook $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; continue; } - // fulltext search in all fields - else if ($col == '*') { - $words = array(); - foreach (explode($WS, rcube_utils::normalize_string($value)) as $word) { - switch ($mode) { - case 1: // strict - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word . $WS . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word) . ')'; - break; - case 2: // prefix - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word . '%') . ')'; - break; - default: // partial - $words[] = $this->db->ilike('words', '%' . $word . '%'); - } - } - $where[] = '(' . join(' AND ', $words) . ')'; - } - else { - $val = is_array($value) ? $value[$idx] : $value; + else if (is_array($value)) { + $val = $value[$idx]; // table column if (in_array($col, $this->table_cols)) { switch ($mode) { @@ -362,27 +342,18 @@ class rcube_contacts extends rcube_addressbook // vCard field else { if (in_array($col, $this->fulltext_cols)) { - foreach (rcube_utils::normalize_string($val, true) as $word) { - switch ($mode) { - case 1: // strict - $words[] = '(' . $this->db->ilike('words', $word . $WS . '%') - . ' OR ' . $this->db->ilike('words', '%' . $AS . $word . $WS .'%') - . ' OR ' . $this->db->ilike('words', '%' . $AS . $word) . ')'; - break; - case 2: // prefix - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', $AS . $word . '%') . ')'; - break; - default: // partial - $words[] = $this->db->ilike('words', '%' . $word . '%'); - } - } - $where[] = '(' . join(' AND ', $words) . ')'; + $where[] = $this->fulltext_sql_where($val, $mode, 'words'); } - if (is_array($value)) - $post_search[$col] = mb_strtolower($val); + $post_search[$col] = mb_strtolower($val); } } + // fulltext search in all fields + else if ($col == '*') { + $where[] = $this->fulltext_sql_where($value, $mode, 'words'); + } + else { + $where[] = $this->fulltext_sql_where($value, $mode, $col, 'OR'); + } } foreach (array_intersect($required, $this->table_cols) as $col) { @@ -391,7 +362,7 @@ class rcube_contacts extends rcube_addressbook if (!empty($where)) { // use AND operator for advanced searches - $where = join(is_array($value) ? ' AND ' : ' OR ', $where); + $where = join(is_array($value) || $fields[0] != '*' ? ' AND ' : ' OR ', $where); } if (!empty($and_where)) @@ -460,6 +431,33 @@ class rcube_contacts extends rcube_addressbook return $this->result; } + /** + * Helper method to compose SQL where statements for fulltext searching + */ + private function fulltext_sql_where($value, $mode, $col = 'words', $bool = 'AND') + { + $WS = ' '; + $AS = $col == 'words' ? $WS : self::SEPARATOR; + + $where = array(); + foreach (rcube_utils::normalize_string($value, true) as $word) { + switch ($mode) { + case 1: // strict + $where[] = '(' . $this->db->ilike($col, $word . '%') + . ' OR ' . $this->db->ilike($col, '%' . $WS . $word . $WS . '%') + . ' OR ' . $this->db->ilike($col, '%' . $WS . $word) . ')'; + break; + case 2: // prefix + $where[] = '(' . $this->db->ilike($col, $word . '%') + . ' OR ' . $this->db->ilike($col, '%' . $AS . $word . '%') . ')'; + break; + default: // partial + $where[] = $this->db->ilike($col, '%' . $word . '%'); + } + } + + return '(' . join(" $bool ", $where) . ')'; + } /** * Count number of available contacts in database -- cgit v1.2.3 From 3bd027726114a4f66dfcf5cbe069d50b9485f010 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 16:28:10 +0100 Subject: Fix full-text searching in a given list of fields --- program/lib/Roundcube/rcube_contacts.php | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 8ba3e6372..5cd96b2dd 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -302,8 +302,6 @@ class rcube_contacts extends rcube_addressbook */ function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array()) { - if (!is_array($fields)) - $fields = array($fields); if (!is_array($required) && !empty($required)) $required = array($required); @@ -312,16 +310,19 @@ class rcube_contacts extends rcube_addressbook $WS = ' '; $AS = self::SEPARATOR; - foreach ($fields as $idx => $col) { - // direct ID search - if ($col == 'ID' || $col == $this->primary_key) { - $ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value; - $ids = $this->db->array2list($ids, 'integer'); - $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; - continue; - } - else if (is_array($value)) { + // direct ID search + if ($fields == 'ID' || $fields == $this->primary_key) { + $ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value; + $ids = $this->db->array2list($ids, 'integer'); + $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; + } + else if (is_array($value)) { + foreach ((array)$fields as $idx => $col) { $val = $value[$idx]; + + if (!strlen($val)) + continue; + // table column if (in_array($col, $this->table_cols)) { switch ($mode) { @@ -347,12 +348,19 @@ class rcube_contacts extends rcube_addressbook $post_search[$col] = mb_strtolower($val); } } - // fulltext search in all fields - else if ($col == '*') { - $where[] = $this->fulltext_sql_where($value, $mode, 'words'); - } - else { - $where[] = $this->fulltext_sql_where($value, $mode, $col, 'OR'); + } + // fulltext search in all fields + else if ($fields == '*') { + $where[] = $this->fulltext_sql_where($value, $mode, 'words'); + } + else { + // require each word in to be present in one of the fields + foreach (rcube_utils::normalize_string($value, true) as $word) { + $groups = array(); + foreach ((array)$fields as $idx => $col) { + $groups[] = $this->fulltext_sql_where($word, $mode, $col); + } + $where[] = '(' . join(' OR ', $groups) . ')'; } } @@ -362,7 +370,7 @@ class rcube_contacts extends rcube_addressbook if (!empty($where)) { // use AND operator for advanced searches - $where = join(is_array($value) || $fields[0] != '*' ? ' AND ' : ' OR ', $where); + $where = join(" AND ", $where); } if (!empty($and_where)) -- cgit v1.2.3 From 8e333bcb662b6464f8a6c873cfb8825ac0a11943 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 16:35:01 +0100 Subject: Only normalize search term when searching in 'words' column --- program/lib/Roundcube/rcube_contacts.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 5cd96b2dd..6438c431a 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -355,7 +355,7 @@ class rcube_contacts extends rcube_addressbook } else { // require each word in to be present in one of the fields - foreach (rcube_utils::normalize_string($value, true) as $word) { + foreach (rcube_utils::tokenize_string($value, 1) as $word) { $groups = array(); foreach ((array)$fields as $idx => $col) { $groups[] = $this->fulltext_sql_where($word, $mode, $col); @@ -446,9 +446,10 @@ class rcube_contacts extends rcube_addressbook { $WS = ' '; $AS = $col == 'words' ? $WS : self::SEPARATOR; + $words = $col == 'words' ? rcube_utils::normalize_string($value, true) : array($value); $where = array(); - foreach (rcube_utils::normalize_string($value, true) as $word) { + foreach ($words as $word) { switch ($mode) { case 1: // strict $where[] = '(' . $this->db->ilike($col, $word . '%') -- 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') 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 6cdffbf1b603f827cca116e2b29716d68543aeb4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 4 Mar 2015 11:55:52 -0500 Subject: Fix "PHP Fatal error: Using $this when not in object context" --- program/lib/Roundcube/rcube_ldap.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index aad0090f8..f492111cc 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -834,10 +834,10 @@ class rcube_ldap extends rcube_addressbook } else { // map address book fields into ldap attributes - $me = $this; + $me = $this; $attributes = array(); array_walk($fields, function($field) use ($me, &$attributes) { - if ($this->coltypes[$field] && ($attrs = (array)$this->coltypes[$field]['attributes'])) { + if ($me->coltypes[$field] && ($attrs = (array)$me->coltypes[$field]['attributes'])) { $attributes = array_merge($attributes, $attrs); } }); -- cgit v1.2.3 From 9af8e22b8ea3bd4fe9e05860058d7fce29019455 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 8 Mar 2015 10:51:19 +0100 Subject: Plugin API: added message_part_body hook, fixes around message structure handling by plugins --- CHANGELOG | 1 + program/lib/Roundcube/rcube_imap.php | 26 +++++++++++++++++++------- program/lib/Roundcube/rcube_message.php | 29 +++++++++++++++++++---------- program/lib/Roundcube/rcube_storage.php | 12 +++++++----- 4 files changed, 46 insertions(+), 22 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index bae36098a..4376812e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Plugin API: Added message_part_body hook - Add initdb.sh to create database from initial.sql script with prefix support (#1490188) - Plugin API: Add special onload() method to execute plugin actions before startup (session and GUI initialization) - Password plugin: Added 'kpasswd' driver by Peter Allgeyer diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index d17b33f6e..65e095099 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1957,6 +1957,16 @@ class rcube_imap extends rcube_storage for ($i=1; $ictype_secondary = strtolower($part[$i]); + + // read content type parameters + if (is_array($part[$i+1])) { + $struct->ctype_parameters = array(); + for ($j=0; $jctype_parameters[$param] = $part[$i+1][$j+1]; + } + } + break; } } @@ -2364,36 +2374,38 @@ class rcube_imap extends rcube_storage /** * Returns the whole message source as string (or saves to a file) * - * @param int $uid Message UID - * @param resource $fp File pointer to save the message + * @param int $uid Message UID + * @param resource $fp File pointer to save the message + * @param string $part Optional message part ID * * @return string Message source string */ - public function get_raw_body($uid, $fp=null) + public function get_raw_body($uid, $fp=null, $part = null) { if (!$this->check_connection()) { return null; } return $this->conn->handlePartBody($this->folder, $uid, - true, null, null, false, $fp); + true, $part, null, false, $fp); } /** * Returns the message headers as string * - * @param int $uid Message UID + * @param int $uid Message UID + * @param string $part Optional message part ID * * @return string Message headers string */ - public function get_raw_headers($uid) + public function get_raw_headers($uid, $part = null) { if (!$this->check_connection()) { return null; } - return $this->conn->fetchPartHeader($this->folder, $uid, true); + return $this->conn->fetchPartHeader($this->folder, $uid, true, $part); } diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 8af334446..b135ae0ff 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -53,13 +53,13 @@ class rcube_message public $uid; public $folder; public $headers; - public $parts = array(); - public $mime_parts = array(); + public $sender; + public $parts = array(); + public $mime_parts = array(); public $inline_parts = array(); - public $attachments = array(); - public $subject = ''; - public $sender = null; - public $is_safe = false; + public $attachments = array(); + public $subject = ''; + public $is_safe = false; const BODY_MAX_SIZE = 1048576; // 1MB @@ -217,6 +217,10 @@ class rcube_message return; } + // allow plugins to modify part body + $plugin = $this->app->plugins->exec_hook('message_part_body', + array('object' => $this, 'part' => $part)); + // only text parts can be formatted $formatted = $formatted && $part->ctype_primary == 'text'; @@ -499,8 +503,9 @@ class rcube_message $structure->headers = rcube_mime::parse_headers($headers); } } - else + else { $mimetype = $structure->mimetype; + } // show message headers if ($recursive && is_array($structure->headers) && @@ -516,11 +521,15 @@ class rcube_message array('object' => $this, 'structure' => $structure, 'mimetype' => $mimetype, 'recursive' => $recursive)); - if ($plugin['abort']) + if ($plugin['abort']) { return; + } $structure = $plugin['structure']; - list($message_ctype_primary, $message_ctype_secondary) = explode('/', $plugin['mimetype']); + $mimetype = $plugin['mimetype']; + $recursive = $plugin['recursive']; + + list($message_ctype_primary, $message_ctype_secondary) = explode('/', $mimetype); // print body if message doesn't have multiple parts if ($message_ctype_primary == 'text' && !$recursive) { @@ -673,7 +682,7 @@ class rcube_message } else { $part_mimetype = $part_orig_mimetype = $mail_part->mimetype; - } + } // multipart/alternative if ($primary_type == 'multipart') { diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index ccb28c680..23e65770d 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -492,22 +492,24 @@ abstract class rcube_storage /** * Returns the whole message source as string (or saves to a file) * - * @param int $uid Message UID - * @param resource $fp File pointer to save the message + * @param int $uid Message UID + * @param resource $fp File pointer to save the message + * @param string $part Optional message part ID * * @return string Message source string */ - abstract function get_raw_body($uid, $fp = null); + abstract function get_raw_body($uid, $fp = null, $part = null); /** * Returns the message headers as string * - * @param int $uid Message UID + * @param int $uid Message UID + * @param string $part Optional message part ID * * @return string Message headers string */ - abstract function get_raw_headers($uid); + abstract function get_raw_headers($uid, $part = null); /** -- cgit v1.2.3 From aafc050f550eec7557d9e66ee90cc8bd7b5536da Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 9 Mar 2015 09:12:34 +0100 Subject: Fix rcube_contacts::search() calls with empty search string but $required argument --- program/lib/Roundcube/rcube_contacts.php | 44 +++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 6438c431a..23e0b710c 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -305,7 +305,7 @@ class rcube_contacts extends rcube_addressbook if (!is_array($required) && !empty($required)) $required = array($required); - $where = $and_where = array(); + $where = $and_where = $post_search = array(); $mode = intval($mode); $WS = ' '; $AS = self::SEPARATOR; @@ -367,6 +367,7 @@ class rcube_contacts extends rcube_addressbook foreach (array_intersect($required, $this->table_cols) as $col) { $and_where[] = $this->db->quote_identifier($col).' <> '.$this->db->quote(''); } + $required = array_diff($required, $this->table_cols); if (!empty($where)) { // use AND operator for advanced searches @@ -378,7 +379,7 @@ class rcube_contacts extends rcube_addressbook // Post-searching in vCard data fields // we will search in all records and then build a where clause for their IDs - if (!empty($post_search)) { + if (!empty($post_search) || !empty($required)) { $ids = array(0); // build key name regexp $regexp = '/^(' . implode(array_keys($post_search), '|') . ')(?:.*)$/'; @@ -387,7 +388,7 @@ class rcube_contacts extends rcube_addressbook $this->set_search_set($where); // count result pages - $cnt = $this->count(); + $cnt = $this->count()->count; $pages = ceil($cnt / $this->page_size); $scnt = count($post_search); @@ -397,14 +398,33 @@ class rcube_contacts extends rcube_addressbook while ($row = $this->result->next()) { $id = $row[$this->primary_key]; $found = array(); - foreach (preg_grep($regexp, array_keys($row)) as $col) { - $pos = strpos($col, ':'); - $colname = $pos ? substr($col, 0, $pos) : $col; - $search = $post_search[$colname]; - foreach ((array)$row[$col] as $value) { - if ($this->compare_search_value($colname, $value, $search, $mode)) { - $found[$colname] = true; - break 2; + if (!empty($post_search)) { + foreach (preg_grep($regexp, array_keys($row)) as $col) { + $pos = strpos($col, ':'); + $colname = $pos ? substr($col, 0, $pos) : $col; + $search = $post_search[$colname]; + foreach ((array)$row[$col] as $value) { + if ($this->compare_search_value($colname, $value, $search, $mode)) { + $found[$colname] = true; + break 2; + } + } + } + } + // check if required fields are present + if (!empty($required)) { + foreach ($required as $req) { + $hit = false; + foreach ($row as $c => $values) { + if ($c === $req || strpos($c, $req.':') === 0) { + if ((is_string($row[$c]) && strlen($row[$c])) || !empty($row[$c])) { + $hit = true; + break; + } + } + } + if (!$hit) { + continue 2; } } } @@ -465,7 +485,7 @@ class rcube_contacts extends rcube_addressbook } } - return '(' . join(" $bool ", $where) . ')'; + return count($where) ? '(' . join(" $bool ", $where) . ')' : ''; } /** -- 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') 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 5def0d8e9ebb698f3622d97ebdd2c51b2748c773 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 11 Mar 2015 16:35:29 +0100 Subject: Ignore plugin loading filter if force parameter is set --- program/lib/Roundcube/rcube_plugin_api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 9bc60f639..ce727e2e7 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -212,7 +212,7 @@ class rcube_plugin_api if ($plugin = $this->plugins[$plugin_name]) { // init a plugin only if $force is set or if we're called after initialization - if (($force || $this->initialized) && !$this->plugins_initialized[$plugin_name] && !$this->filter($plugin)) { + if (($force || $this->initialized) && !$this->plugins_initialized[$plugin_name] && ($force || !$this->filter($plugin))) { $plugin->init(); $this->plugins_initialized[$plugin_name] = $plugin; } -- cgit v1.2.3 From 496972bf95e2ddbf01cb5e50a6a594615744d942 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 12 Mar 2015 09:44:31 +0100 Subject: Fix backtick character handling in sql queries (#1490312) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_db.php | 12 ++-- program/lib/Roundcube/rcube_db_oracle.php | 9 ++- tests/Framework/DB.php | 108 +++++++++++++++++++++++++++--- 4 files changed, 112 insertions(+), 18 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index f94e5343d..9ede28d7f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ CHANGELOG Roundcube Webmail - Fix rows count when messages search fails (#1490266) - Fix bug where spellchecking in HTML editor do not work after switching editor type more than once (#1490311) - Fix bug where TinyMCE area height was too small on slow network connection (#1490310) +- Fix backtick character handling in sql queries (#1490312) RELEASE 1.1.0 ------------- diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index 2cacb3013..a31b2005d 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -448,10 +448,15 @@ class rcube_db } } - // replace escaped '?' back to normal, see self::quote() - $query = str_replace('??', '?', $query); $query = rtrim($query, " \t\n\r\0\x0B;"); + // replace escaped '?' and quotes back to normal, see self::quote() + $query = str_replace( + array('??', self::DEFAULT_QUOTE.self::DEFAULT_QUOTE), + array('?', self::DEFAULT_QUOTE), + $query + ); + // log query $this->debug($query); @@ -516,9 +521,6 @@ class rcube_db } } - // replace escaped quote back to normal, see self::quote() - $query = str_replace($quote.$quote, $quote, $query); - return $query; } diff --git a/program/lib/Roundcube/rcube_db_oracle.php b/program/lib/Roundcube/rcube_db_oracle.php index 34e4e69f8..bb033884c 100644 --- a/program/lib/Roundcube/rcube_db_oracle.php +++ b/program/lib/Roundcube/rcube_db_oracle.php @@ -155,10 +155,15 @@ class rcube_db_oracle extends rcube_db } } - // replace escaped '?' back to normal, see self::quote() - $query = str_replace('??', '?', $query); $query = rtrim($query, " \t\n\r\0\x0B;"); + // replace escaped '?' and quotes back to normal, see self::quote() + $query = str_replace( + array('??', self::DEFAULT_QUOTE.self::DEFAULT_QUOTE), + array('?', self::DEFAULT_QUOTE), + $query + ); + // log query $this->debug($query); diff --git a/tests/Framework/DB.php b/tests/Framework/DB.php index 42020f47a..04897bb90 100644 --- a/tests/Framework/DB.php +++ b/tests/Framework/DB.php @@ -25,6 +25,8 @@ class Framework_DB extends PHPUnit_Framework_TestCase { $db = new rcube_db_test_wrapper('test'); $db->set_option('table_prefix', 'prefix_'); + $db->set_option('identifier_start', '`'); + $db->set_option('identifier_end', '`'); $script = implode("\n", array( "CREATE TABLE `xxx` (test int, INDEX xxx (test));", @@ -38,26 +40,88 @@ class Framework_DB extends PHPUnit_Framework_TestCase "SELECT test FROM xxx;", )); $output = implode("\n", array( - "CREATE TABLE `prefix_xxx` (test int, INDEX prefix_xxx (test));", - "ALTER TABLE `prefix_xxx` CHANGE test test int;", - "TRUNCATE prefix_xxx;", - "DROP TABLE `prefix_vvv`;", + "CREATE TABLE `prefix_xxx` (test int, INDEX prefix_xxx (test))", + "ALTER TABLE `prefix_xxx` CHANGE test test int", + "TRUNCATE prefix_xxx", + "DROP TABLE `prefix_vvv`", "CREATE TABLE `prefix_i` (test int CONSTRAINT `prefix_iii` - FOREIGN KEY (`test`) REFERENCES `prefix_xxx`(`test`) ON DELETE CASCADE ON UPDATE CASCADE);", - "INSERT INTO prefix_xxx test = 1;", - "SELECT test FROM prefix_xxx;", + FOREIGN KEY (`test`) REFERENCES `prefix_xxx`(`test`) ON DELETE CASCADE ON UPDATE CASCADE)", + "INSERT INTO prefix_xxx test = 1", + "SELECT test FROM prefix_xxx", )); $result = $db->exec_script($script); - $out = ''; + $out = array(); foreach ($db->queries as $q) { - $out[] = $q[0]; + $out[] = $q; } $this->assertTrue($result, "Execute SQL script (result)"); $this->assertSame(implode("\n", $out), $output, "Execute SQL script (content)"); } + + /** + * Test query parsing and arguments quoting + */ + function test_query_parsing() + { + $db = new rcube_db_test_wrapper('test'); + $db->set_option('identifier_start', '`'); + $db->set_option('identifier_end', '`'); + + $db->query("SELECT ?", "test`test"); + $db->query("SELECT ?", "test?test"); + $db->query("SELECT ?", "test``test"); + $db->query("SELECT ?", "test??test"); + $db->query("SELECT `test` WHERE 'test``test'"); + $db->query("SELECT `test` WHERE 'test??test'"); + $db->query("SELECT `test` WHERE `test` = ?", "`te``st`"); + $db->query("SELECT `test` WHERE `test` = ?", "?test?"); + $db->query("SELECT `test` WHERE `test` = ?", "????"); + + $expected = implode("\n", array( + "SELECT 'test`test'", + "SELECT 'test?test'", + "SELECT 'test``test'", + "SELECT 'test??test'", + "SELECT `test` WHERE 'test`test'", + "SELECT `test` WHERE 'test?test'", + "SELECT `test` WHERE `test` = '`te``st`'", + "SELECT `test` WHERE `test` = '?test?'", + "SELECT `test` WHERE `test` = '????'", + )); + + $this->assertSame($expected, implode("\n", $db->queries), "Query parsing [1]"); + + $db->set_option('identifier_start', '"'); + $db->set_option('identifier_end', '"'); + $db->queries = array(); + + $db->query("SELECT ?", "test`test"); + $db->query("SELECT ?", "test?test"); + $db->query("SELECT ?", "test``test"); + $db->query("SELECT ?", "test??test"); + $db->query("SELECT `test` WHERE 'test``test'"); + $db->query("SELECT `test` WHERE 'test??test'"); + $db->query("SELECT `test` WHERE `test` = ?", "`te``st`"); + $db->query("SELECT `test` WHERE `test` = ?", "?test?"); + $db->query("SELECT `test` WHERE `test` = ?", "????"); + + $expected = implode("\n", array( + "SELECT 'test`test'", + "SELECT 'test?test'", + "SELECT 'test``test'", + "SELECT 'test??test'", + "SELECT \"test\" WHERE 'test`test'", + "SELECT \"test\" WHERE 'test?test'", + "SELECT \"test\" WHERE \"test\" = '`te``st`'", + "SELECT \"test\" WHERE \"test\" = '?test?'", + "SELECT \"test\" WHERE \"test\" = '????'", + )); + + $this->assertSame($expected, implode("\n", $db->queries), "Query parsing [2]"); + } } /** @@ -67,8 +131,30 @@ class rcube_db_test_wrapper extends rcube_db { public $queries = array(); - protected function _query($query, $offset, $numrows, $params) + protected function query_execute($query) + { + $this->queries[] = $query; + } + + public function db_connect($mode, $force = false) + { + $this->dbh = new rcube_db_test_dbh(); + } + + public function is_connected() + { + return true; + } + + protected function debug($data) + { + } +} + +class rcube_db_test_dbh +{ + public function quote($data, $type) { - $this->queries[] = array(trim($query), $offset, $numrows, $params); + return "'$data'"; } } -- cgit v1.2.3 From a74d02390353ba6294297ed3e76e4ed47841f9b2 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 12 Mar 2015 09:47:43 +0100 Subject: Generate random hash for the per-user local storage prefix (#1490279); only unserialize user prefs once --- program/lib/Roundcube/rcube_user.php | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_user.php b/program/lib/Roundcube/rcube_user.php index 77c58dd14..1a61efd5e 100644 --- a/program/lib/Roundcube/rcube_user.php +++ b/program/lib/Roundcube/rcube_user.php @@ -29,6 +29,7 @@ class rcube_user public $ID; public $data; public $language; + public $prefs; /** * Holds database connection. @@ -132,10 +133,14 @@ class rcube_user */ function get_prefs() { - $prefs = array(); + if (isset($this->prefs)) { + return $this->prefs; + } + + $this->prefs = array(); if (!empty($this->language)) - $prefs['language'] = $this->language; + $this->prefs['language'] = $this->language; if ($this->ID) { // Preferences from session (write-master is unavailable) @@ -153,11 +158,11 @@ class rcube_user } if ($this->data['preferences']) { - $prefs += (array)unserialize($this->data['preferences']); + $this->prefs += (array)unserialize($this->data['preferences']); } } - return $prefs; + return $this->prefs; } /** @@ -183,7 +188,7 @@ class rcube_user $config = $this->rc->config; // merge (partial) prefs array with existing settings - $save_prefs = $a_user_prefs + $old_prefs; + $this->prefs = $save_prefs = $a_user_prefs + $old_prefs; unset($save_prefs['language']); // don't save prefs with default values if they haven't been changed yet @@ -229,12 +234,20 @@ class rcube_user } /** - * Generate a unique hash to identify this user which + * Generate a unique hash to identify this user whith */ function get_hash() { - $key = substr($this->rc->config->get('des_key'), 1, 4); - return md5($this->data['user_id'] . $key . $this->data['username'] . '@' . $this->data['mail_host']); + $prefs = $this->get_prefs(); + + // generate a random hash and store it in user prefs + if (empty($prefs['client_hash'])) { + mt_srand((double)microtime() * 1000000); + $prefs['client_hash'] = md5($this->data['username'] . mt_rand() . $this->data['mail_host']); + $this->save_prefs(array('client_hash' => $prefs['client_hash'])); + } + + return $prefs['client_hash']; } /** -- cgit v1.2.3 From e2fb34028980910e006f09b4fd93c4172f79b306 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 12 Mar 2015 09:59:47 +0100 Subject: Remove obsolete mt_srand() calls --- plugins/password/drivers/ldap.php | 3 --- program/lib/Roundcube/rcube_user.php | 1 - 2 files changed, 4 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php index a11c38d17..6ed5ada04 100644 --- a/plugins/password/drivers/ldap.php +++ b/plugins/password/drivers/ldap.php @@ -289,7 +289,6 @@ class rcube_ldap_password break; case 'ssha': - mt_srand((double) microtime() * 1000000); $salt = substr(pack('h*', md5(mt_rand())), 0, 8); if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { @@ -316,7 +315,6 @@ class rcube_ldap_password case 'smd5': - mt_srand((double) microtime() * 1000000); $salt = substr(pack('h*', md5(mt_rand())), 0, 8); if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { @@ -373,7 +371,6 @@ class rcube_ldap_password { $possible = '0123456789' . 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . './'; $str = ''; - // mt_srand((double)microtime() * 1000000); while (strlen($str) < $length) { $str .= substr($possible, (rand() % strlen($possible)), 1); diff --git a/program/lib/Roundcube/rcube_user.php b/program/lib/Roundcube/rcube_user.php index 1a61efd5e..8ed34fc28 100644 --- a/program/lib/Roundcube/rcube_user.php +++ b/program/lib/Roundcube/rcube_user.php @@ -242,7 +242,6 @@ class rcube_user // generate a random hash and store it in user prefs if (empty($prefs['client_hash'])) { - mt_srand((double)microtime() * 1000000); $prefs['client_hash'] = md5($this->data['username'] . mt_rand() . $this->data['mail_host']); $this->save_prefs(array('client_hash' => $prefs['client_hash'])); } -- 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') 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 From 3d6ce950097e591017514fb9400839c89fd589d9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 1 Apr 2015 09:46:29 +0200 Subject: Fix PHP warning: Non-static method PEAR::setErrorHandling() should not be called statically (#1490343) --- CHANGELOG | 1 + program/lib/Roundcube/bootstrap.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 41cd629de..428238269 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ CHANGELOG Roundcube Webmail - Fix handling of %-encoded entities in mailto: URLs (#1490346) - Fix zipped messages downloads after selecting all messages in a folder (#1490339) - Fix vpopmaild driver of password plugin +- Fix PHP warning: Non-static method PEAR::setErrorHandling() should not be called statically (#1490343) RELEASE 1.1.1 ------------- diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php index 0c950dc14..ba7954e84 100644 --- a/program/lib/Roundcube/bootstrap.php +++ b/program/lib/Roundcube/bootstrap.php @@ -99,7 +99,7 @@ if (!preg_match($regexp, $path)) { spl_autoload_register('rcube_autoload'); // set PEAR error handling (will also load the PEAR main class) -PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error'); +@PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error'); -- cgit v1.2.3 From 5a4f3f8266999674baeccfba0df8f911372b55a7 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 3 Apr 2015 17:46:39 +0200 Subject: Remove Kolab's hack for Cyrus Murder bug in SETANNOTATION - it's not needed anymore --- program/lib/Roundcube/rcube_imap_generic.php | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index 0058bf487..ad7f2db27 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -48,8 +48,6 @@ class rcube_imap_generic '*' => '\\*', ); - public static $mupdate; - protected $fp; protected $host; protected $logged = false; @@ -3271,11 +3269,6 @@ class rcube_imap_generic } foreach ($data as $entry) { - // Workaround cyrus-murder bug, the entry[2] string needs to be escaped - if (self::$mupdate) { - $entry[2] = addcslashes($entry[2], '\\"'); - } - // ANNOTATEMORE drafts before version 08 require quoted parameters $entries[] = sprintf('%s (%s %s)', $this->escape($entry[0], true), $this->escape($entry[1], true), $this->escape($entry[2], true)); @@ -3840,10 +3833,6 @@ class rcube_imap_generic $this->prefs['literal+'] = true; } - if (preg_match('/(\[| )MUPDATE=.*/', $str)) { - self::$mupdate = true; - } - if ($trusted) { $this->capability_readed = true; } -- cgit v1.2.3 From 48d01837a0a5725d2779f30d20478e77572e9ac5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 6 Apr 2015 12:00:09 +0200 Subject: Fix tables listing routine on mysql and postgres so it skips system or other database tables and views (#1490337) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_db.php | 11 ++++------- program/lib/Roundcube/rcube_db_mysql.php | 24 ++++++++++++++++++++++++ program/lib/Roundcube/rcube_db_pgsql.php | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 7 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 428238269..d79610b2d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ CHANGELOG Roundcube Webmail - Fix zipped messages downloads after selecting all messages in a folder (#1490339) - Fix vpopmaild driver of password plugin - Fix PHP warning: Non-static method PEAR::setErrorHandling() should not be called statically (#1490343) +- Fix tables listing routine on mysql and postgres so it skips system or other database tables and views (#1490337) RELEASE 1.1.1 ------------- diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index a31b2005d..4ccc59ba3 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -691,14 +691,11 @@ class rcube_db { // get tables if not cached if ($this->tables === null) { - $q = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME'); + $q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES" + . " WHERE TABLE_TYPE = 'BASE TABLE'" + . " ORDER BY TABLE_NAME"); - if ($q) { - $this->tables = $q->fetchAll(PDO::FETCH_COLUMN, 0); - } - else { - $this->tables = array(); - } + $this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : array(); } return $this->tables; diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index dd28c25c8..616d1752b 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -149,6 +149,30 @@ class rcube_db_mysql extends rcube_db return $result; } + /** + * Returns list of tables in a database + * + * @return array List of all tables of the current database + */ + public function list_tables() + { + // get tables if not cached + if ($this->tables === null) { + // first fetch current database name + $d = $this->query("SELECT database()"); + $d = $this->fetch_array($d); + + // get list of tables in current database + $q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES" + . " WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE'" + . " ORDER BY TABLE_NAME", $d ? $d[0] : ''); + + $this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : array(); + } + + return $this->tables; + } + /** * Get database runtime variables * diff --git a/program/lib/Roundcube/rcube_db_pgsql.php b/program/lib/Roundcube/rcube_db_pgsql.php index ff41df224..b4255513b 100644 --- a/program/lib/Roundcube/rcube_db_pgsql.php +++ b/program/lib/Roundcube/rcube_db_pgsql.php @@ -157,6 +157,25 @@ class rcube_db_pgsql extends rcube_db return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; } + /** + * Returns list of tables in a database + * + * @return array List of all tables of the current database + */ + public function list_tables() + { + // get tables if not cached + if ($this->tables === null) { + $q = $this->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES" + . " WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('pg_catalog', 'information_schema')" + . " ORDER BY TABLE_NAME"); + + $this->tables = $q ? $q->fetchAll(PDO::FETCH_COLUMN, 0) : array(); + } + + return $this->tables; + } + /** * Returns PDO DSN string from DSN array * -- cgit v1.2.3 From 98e4614b0da073b04ba54bfee1df3491dcea6762 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 6 Apr 2015 13:10:06 +0200 Subject: Fix so text/calendar parts are listed as attachments even if not marked as such (#1490325) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_message.php | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 9b89a46a9..216a29b67 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ CHANGELOG Roundcube Webmail - Fix PHP warning: Non-static method PEAR::setErrorHandling() should not be called statically (#1490343) - Fix tables listing routine on mysql and postgres so it skips system or other database tables and views (#1490337) - Fix message list header in classic skin on window resize in Internet Explorer (#1490213) +- Fix so text/calendar parts are listed as attachments even if not marked as such (#1490325) RELEASE 1.1.1 ------------- diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index b135ae0ff..449a3c3b4 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -791,6 +791,14 @@ class rcube_message else if ($mail_part->mimetype == 'message/rfc822') { $this->parse_structure($mail_part); } + // calendar part not marked as attachment (#1490325) + else if ($part_mimetype == 'text/calendar') { + if (!$mail_part->filename) { + $mail_part->filename = 'calendar.ics'; + } + + $this->attachments[] = $mail_part; + } } // if this was a related part try to resolve references -- cgit v1.2.3 From 292292df32a5d3f8db2cfc266fb4a8f7dbf52468 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 8 Apr 2015 08:56:42 +0200 Subject: Fix so unrecognized TNEF attachments are displayed on the list of attachments (#1490351) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_message.php | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/CHANGELOG b/CHANGELOG index 216a29b67..6dbbecf7a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ CHANGELOG Roundcube Webmail - Fix tables listing routine on mysql and postgres so it skips system or other database tables and views (#1490337) - Fix message list header in classic skin on window resize in Internet Explorer (#1490213) - Fix so text/calendar parts are listed as attachments even if not marked as such (#1490325) +- Fix so unrecognized TNEF attachments are displayed on the list of attachments (#1490351) RELEASE 1.1.1 ------------- diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 449a3c3b4..6c6248ebe 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -739,10 +739,19 @@ class rcube_message } // part is Microsoft Outlook TNEF (winmail.dat) else if ($part_mimetype == 'application/ms-tnef') { - foreach ((array)$this->tnef_decode($mail_part) as $tpart) { + $tnef_parts = (array) $this->tnef_decode($mail_part); + foreach ($tnef_parts as $tpart) { $this->mime_parts[$tpart->mime_id] = $tpart; $this->attachments[] = $tpart; } + + // add winmail.dat to the list if it's content is unknown + if (empty($tnef_parts) && !empty($mail_part->filename)) { + $this->mime_parts[$mail_part->mime_id] = $mail_part; + $this->attachments[] = $mail_part; + } + + $tnef_parts = null; } // part is a file/attachment else if (preg_match('/^(inline|attach)/', $mail_part->disposition) || -- cgit v1.2.3 From d61d668b64c44fc046095b807834c4836a8c05c5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 8 Apr 2015 08:57:21 +0200 Subject: Remove useless code --- program/lib/Roundcube/rcube_message.php | 2 -- 1 file changed, 2 deletions(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 6c6248ebe..26248a5b1 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -750,8 +750,6 @@ class rcube_message $this->mime_parts[$mail_part->mime_id] = $mail_part; $this->attachments[] = $mail_part; } - - $tnef_parts = null; } // part is a file/attachment else if (preg_match('/^(inline|attach)/', $mail_part->disposition) || -- cgit v1.2.3 From 53cbebf8ade5d9994479370cc78ac4e10c23c887 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 9 Apr 2015 10:11:00 +0200 Subject: Fix handling spaces after
on html2text conversion --- program/lib/Roundcube/rcube_html2text.php | 2 +- tests/Framework/Html2text.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube') diff --git a/program/lib/Roundcube/rcube_html2text.php b/program/lib/Roundcube/rcube_html2text.php index 284e50dca..00a59a7f3 100644 --- a/program/lib/Roundcube/rcube_html2text.php +++ b/program/lib/Roundcube/rcube_html2text.php @@ -142,7 +142,7 @@ class rcube_html2text '/]*>.*?<\/script>/i', //