diff options
Diffstat (limited to 'program/lib')
21 files changed, 616 insertions, 295 deletions
diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php index b7e69cb2a..5a371d2f0 100644 --- a/program/lib/Roundcube/bootstrap.php +++ b/program/lib/Roundcube/bootstrap.php @@ -293,32 +293,6 @@ function is_ascii($str, $control_chars = true) /** - * Remove single and double quotes from a given string - * - * @param string Input value - * - * @return string Dequoted string - */ -function strip_quotes($str) -{ - return str_replace(array("'", '"'), '', $str); -} - - -/** - * Remove new lines characters from given string - * - * @param string $str Input value - * - * @return string Stripped string - */ -function strip_newlines($str) -{ - return preg_replace('/[\r\n]/', '', $str); -} - - -/** * Compose a valid representation of name and e-mail address * * @param string $email E-mail address diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 4471acec0..8d17827be 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -457,30 +457,40 @@ class rcube ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); - ini_set('session.serialize_handler', 'php'); ini_set('session.cookie_httponly', 1); // use database for storing session data $this->session = new rcube_session($this->get_dbh(), $this->config); - $this->session->register_gc_handler(array($this, 'temp_gc')); - $this->session->register_gc_handler(array($this, 'cache_gc')); - + $this->session->register_gc_handler(array($this, 'gc_handler')); $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); $this->session->set_ip_check($this->config->get('ip_check')); // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { - session_start(); + $this->session->start(); } } /** + * Garbage collector - cache/temp cleaner + */ + public function gc() + { + rcube_cache::gc(); + rcube_cache_shared::gc(); + $this->get_storage()->cache_gc(); + + $this->gc_temp(); + } + + + /** * Garbage collector function for temp files. * Remove temp files older than two days */ - public function temp_gc() + public function gc_temp() { $tmp = unslashify($this->config->get('temp_dir')); $expire = time() - 172800; // expire in 48 hours @@ -505,7 +515,7 @@ class rcube * Garbage collector for cache entries. * Set flag to expunge caches on shutdown */ - public function cache_gc() + public function gc_handler() { // because this gc function is called before storage is initialized, // we just set a flag to expunge storage cache on shutdown. @@ -514,6 +524,25 @@ class rcube /** + * Runs garbage collector with probability based on + * session settings. This is intended for environments + * without a session. + */ + public function gc_run() + { + $probability = (int) ini_get('session.gc_probability'); + $divisor = (int) ini_get('session.gc_divisor'); + + if ($divisor > 0 && $probability > 0) { + $random = mt_rand(1, $divisor); + if ($random <= $probability) { + $this->gc(); + } + } + } + + + /** * Get localized text in the desired language * * @param mixed $attrib Named parameters array or label name @@ -897,19 +926,17 @@ class rcube $this->smtp->disconnect(); } + if ($this->expunge_cache) { + $this->gc(); + } + foreach ($this->caches as $cache) { if (is_object($cache)) { - if ($this->expunge_cache) { - $cache->expunge(); - } $cache->close(); } } if (is_object($this->storage)) { - if ($this->expunge_cache) { - $this->storage->expunge_cache(); - } $this->storage->close(); } } diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php index 129f3242d..a708cb292 100644 --- a/program/lib/Roundcube/rcube_cache.php +++ b/program/lib/Roundcube/rcube_cache.php @@ -38,6 +38,7 @@ class rcube_cache private $type; private $userid; private $prefix; + private $table; private $ttl; private $packed; private $index; @@ -71,8 +72,9 @@ class rcube_cache $this->db = function_exists('apc_exists'); // APC 3.1.4 required } else { - $this->type = 'db'; - $this->db = $rcube->get_dbh(); + $this->type = 'db'; + $this->db = $rcube->get_dbh(); + $this->table = $this->db->table_name('cache'); } // convert ttl string to seconds @@ -145,7 +147,7 @@ class rcube_cache */ function write($key, $data) { - return $this->write_record($key, $this->packed ? serialize($data) : $data); + return $this->write_record($key, $this->serialize($data)); } @@ -194,18 +196,29 @@ class rcube_cache { if ($this->type == 'db' && $this->db && $this->ttl) { $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM ".$this->table. " WHERE user_id = ?". " AND cache_key LIKE ?". - " AND " . $this->db->unixtimestamp('created')." < ?", + " AND expires < " . $this->db->now(), $this->userid, - $this->prefix.'.%', - time() - $this->ttl); + $this->prefix.'.%'); } } /** + * Remove expired records of all caches + */ + static function gc() + { + $rcube = rcube::get_instance(); + $db = $rcube->get_dbh(); + + $db->query("DELETE FROM " . $db->table_name('cache') . " WHERE expires < " . $db->now()); + } + + + /** * Writes the cache back to the DB. */ function close() @@ -219,7 +232,7 @@ class rcube_cache if ($this->cache_changes[$key]) { // Make sure we're not going to write unchanged data // by comparing current md5 sum with the sum calculated on DB read - $data = $this->packed ? serialize($data) : $data; + $data = $this->serialize($data); if (!$this->cache_sums[$key] || $this->cache_sums[$key] != md5($data)) { $this->write_record($key, $data); @@ -255,7 +268,7 @@ class rcube_cache if ($data) { $md5sum = md5($data); - $data = $this->packed ? unserialize($data) : $data; + $data = $this->unserialize($data); if ($nostore) { return $data; @@ -271,7 +284,7 @@ class rcube_cache else { $sql_result = $this->db->limitquery( "SELECT data, cache_key". - " FROM ".$this->db->table_name('cache'). + " FROM " . $this->table. " WHERE user_id = ?". " AND cache_key = ?". // for better performance we allow more records for one key @@ -283,7 +296,7 @@ class rcube_cache $key = substr($sql_arr['cache_key'], strlen($this->prefix)+1); $md5sum = $sql_arr['data'] ? md5($sql_arr['data']) : null; if ($sql_arr['data']) { - $data = $this->packed ? unserialize($sql_arr['data']) : $sql_arr['data']; + $data = $this->unserialize($sql_arr['data']); } if ($nostore) { @@ -326,7 +339,7 @@ class rcube_cache // Remove NULL rows (here we don't need to check if the record exist) if ($data == 'N;') { $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM " . $this->table. " WHERE user_id = ?". " AND cache_key = ?", $this->userid, $key); @@ -337,8 +350,10 @@ class rcube_cache // update existing cache record if ($key_exists) { $result = $this->db->query( - "UPDATE ".$this->db->table_name('cache'). - " SET created = ". $this->db->now().", data = ?". + "UPDATE " . $this->table. + " SET created = " . $this->db->now(). + ", expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL'). + ", data = ?". " WHERE user_id = ?". " AND cache_key = ?", $data, $this->userid, $key); @@ -348,9 +363,9 @@ class rcube_cache // for better performance we allow more records for one key // so, no need to check if record exist (see rcube_cache::read_record()) $result = $this->db->query( - "INSERT INTO ".$this->db->table_name('cache'). - " (created, user_id, cache_key, data)". - " VALUES (".$this->db->now().", ?, ?, ?)", + "INSERT INTO " . $this->table. + " (created, expires, user_id, cache_key, data)". + " VALUES (" . $this->db->now() . ", " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?, ?, ?)", $this->userid, $key, $data); } @@ -364,7 +379,6 @@ class rcube_cache * @param string $key Cache key name or pattern * @param boolean $prefix_mode Enable it to clear all keys starting * with prefix specified in $key - * */ private function remove_record($key=null, $prefix_mode=false) { @@ -412,7 +426,7 @@ class rcube_cache } $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM " . $this->table. " WHERE user_id = ?" . $where, $this->userid); } @@ -553,4 +567,28 @@ class rcube_cache // This way each cache will have its own index return sprintf('%d:%s%s', $this->userid, $this->prefix, 'INDEX'); } + + /** + * Serializes data for storing + */ + private function serialize($data) + { + if ($this->type == 'db') { + return $this->db->encode($data, $this->packed); + } + + return $this->packed ? serialize($data) : $data; + } + + /** + * Unserializes serialized data + */ + private function unserialize($data) + { + if ($this->type == 'db') { + return $this->db->decode($data, $this->packed); + } + + return $this->packed ? @unserialize($data) : $data; + } } diff --git a/program/lib/Roundcube/rcube_cache_shared.php b/program/lib/Roundcube/rcube_cache_shared.php index 5983bd36b..8f2574046 100644 --- a/program/lib/Roundcube/rcube_cache_shared.php +++ b/program/lib/Roundcube/rcube_cache_shared.php @@ -144,7 +144,7 @@ class rcube_cache_shared */ function write($key, $data) { - return $this->write_record($key, $this->packed ? serialize($data) : $data); + return $this->write_record($key, $this->serialize($data)); } @@ -195,14 +195,25 @@ class rcube_cache_shared $this->db->query( "DELETE FROM " . $this->table . " WHERE cache_key LIKE ?" - . " AND " . $this->db->unixtimestamp('created') . " < ?", - $this->prefix . '.%', - time() - $this->ttl); + . " AND expires < " . $this->db->now(), + $this->prefix . '.%'); } } /** + * Remove expired records of all caches + */ + static function gc() + { + $rcube = rcube::get_instance(); + $db = $rcube->get_dbh(); + + $db->query("DELETE FROM " . $db->table_name('cache_shared') . " WHERE expires < " . $db->now()); + } + + + /** * Writes the cache back to the DB. */ function close() @@ -216,7 +227,7 @@ class rcube_cache_shared if ($this->cache_changes[$key]) { // Make sure we're not going to write unchanged data // by comparing current md5 sum with the sum calculated on DB read - $data = $this->packed ? serialize($data) : $data; + $data = $this->serialize($data); if (!$this->cache_sums[$key] || $this->cache_sums[$key] != md5($data)) { $this->write_record($key, $data); @@ -252,7 +263,7 @@ class rcube_cache_shared if ($data) { $md5sum = md5($data); - $data = $this->packed ? unserialize($data) : $data; + $data = $this->unserialize($data); if ($nostore) { return $data; @@ -278,7 +289,7 @@ class rcube_cache_shared if ($sql_arr = $this->db->fetch_assoc($sql_result)) { $md5sum = $sql_arr['data'] ? md5($sql_arr['data']) : null; if ($sql_arr['data']) { - $data = $this->packed ? unserialize($sql_arr['data']) : $sql_arr['data']; + $data = $this->unserialize($sql_arr['data']); } if ($nostore) { @@ -328,7 +339,9 @@ class rcube_cache_shared if ($key_exists) { $result = $this->db->query( "UPDATE " . $this->table . - " SET created = " . $this->db->now() . ", data = ?" . + " SET created = " . $this->db->now() . + ", expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . + ", data = ?". " WHERE cache_key = ?", $data, $key); } @@ -338,8 +351,8 @@ class rcube_cache_shared // so, no need to check if record exist (see rcube_cache::read_record()) $result = $this->db->query( "INSERT INTO ".$this->table. - " (created, cache_key, data)". - " VALUES (".$this->db->now().", ?, ?)", + " (created, expires, cache_key, data)". + " VALUES (".$this->db->now().", " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?, ?)", $key, $data); } @@ -541,4 +554,28 @@ class rcube_cache_shared // This way each cache will have its own index return $this->prefix . 'INDEX'; } + + /** + * Serializes data for storing + */ + private function serialize($data) + { + if ($this->type == 'db') { + return $this->db->encode($data, $this->packed); + } + + return $this->packed ? serialize($data) : $data; + } + + /** + * Unserializes serialized data + */ + private function unserialize($data) + { + if ($this->type == 'db') { + return $this->db->decode($data, $this->packed); + } + + return $this->packed ? @unserialize($data) : $data; + } } diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php index 2190dc4c2..bd97583c3 100644 --- a/program/lib/Roundcube/rcube_config.php +++ b/program/lib/Roundcube/rcube_config.php @@ -43,6 +43,7 @@ class rcube_config 'reply_mode' => 'top_posting', 'refresh_interval' => 'keep_alive', 'min_refresh_interval' => 'min_keep_alive', + 'messages_cache_ttl' => 'message_cache_lifetime', ); @@ -174,7 +175,7 @@ class rcube_config ob_end_clean(); if (is_array($rcmail_config)) { - $this->prop = array_merge($this->prop, $rcmail_config, $this->userprefs); + $this->merge($rcmail_config); return true; } } @@ -195,9 +196,6 @@ class rcube_config if (isset($this->prop[$name])) { $result = $this->prop[$name]; } - else if (isset($this->legacy_props[$name])) { - return $this->get($this->legacy_props[$name], $def); - } else { $result = $def; } @@ -241,6 +239,7 @@ class rcube_config public function merge($prefs) { $this->prop = array_merge($this->prop, $prefs, $this->userprefs); + $this->fix_legacy_props(); } @@ -273,6 +272,8 @@ class rcube_config $this->userprefs = $prefs; $this->prop = array_merge($this->prop, $prefs); + $this->fix_legacy_props(); + // override timezone settings with client values if ($this->prop['timezone'] == 'auto') { $this->prop['_timezone_value'] = isset($_SESSION['timezone']) ? $this->client_timezone() : $this->prop['_timezone_value']; @@ -435,4 +436,18 @@ class rcube_config return date_default_timezone_get(); } + /** + * Convert legacy options into new ones + */ + private function fix_legacy_props() + { + foreach ($this->legacy_props as $new => $old) { + if (isset($this->prop[$old])) { + if (!isset($this->prop[$new])) { + $this->prop[$new] = $this->prop[$old]; + } + unset($this->prop[$old]); + } + } + } } diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index 5da38c899..852070073 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -100,27 +100,15 @@ class rcube_db $this->db_dsnw_array = self::parse_dsn($db_dsnw); $this->db_dsnr_array = self::parse_dsn($db_dsnr); - - // Initialize driver class - $this->init(); - } - - /** - * Initialization of the object with driver specific code - */ - protected function init() - { - // To be used by driver classes } /** * Connect to specific database * - * @param array $dsn DSN for DB connections - * - * @return PDO database handle + * @param array $dsn DSN for DB connections + * @param string $mode Connection mode (r|w) */ - protected function dsn_connect($dsn) + protected function dsn_connect($dsn, $mode) { $this->db_error = false; $this->db_error_msg = null; @@ -158,9 +146,10 @@ class rcube_db return null; } + $this->dbh = $dbh; + $this->db_mode = $mode; + $this->db_connected = true; $this->conn_configure($dsn, $dbh); - - return $dbh; } /** @@ -183,16 +172,6 @@ class rcube_db } /** - * Driver-specific database character set setting - * - * @param string $charset Character set name - */ - protected function set_charset($charset) - { - $this->query("SET NAMES 'utf8'"); - } - - /** * Connect to appropriate database depending on the operation * * @param string $mode Connection mode (r|w) @@ -219,23 +198,14 @@ class rcube_db $dsn = ($mode == 'r') ? $this->db_dsnr_array : $this->db_dsnw_array; - $this->dbh = $this->dsn_connect($dsn); - $this->db_connected = is_object($this->dbh); + $this->dsn_connect($dsn, $mode); // use write-master when read-only fails if (!$this->db_connected && $mode == 'r' && $this->is_replicated()) { - $mode = 'w'; - $this->dbh = $this->dsn_connect($this->db_dsnw_array); - $this->db_connected = is_object($this->dbh); + $this->dsn_connect($this->db_dsnw_array, 'w'); } - if ($this->db_connected) { - $this->db_mode = $mode; - $this->set_charset('utf8'); - } - else { - $this->conn_failure = true; - } + $this->conn_failure = !$this->db_connected; } /** @@ -368,8 +338,10 @@ class rcube_db */ protected function _query($query, $offset, $numrows, $params) { + $query = trim($query); + // Read or write ? - $mode = preg_match('/^(select|show)/i', ltrim($query)) ? 'r' : 'w'; + $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w'; $this->db_connect($mode); @@ -415,13 +387,16 @@ class rcube_db if ($result === false) { $error = $this->dbh->errorInfo(); - $this->db_error = true; - $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg . " (SQL Query: $query)" - ), true, false); + if (empty($this->options['ignore_key_errors']) || $error[0] != '23000') { + $this->db_error = true; + $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); + + rcube::raise_error(array('code' => 500, 'type' => 'db', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => $this->db_error_msg . " (SQL Query: $query)" + ), true, false); + } } $this->last_result = $result; @@ -708,11 +683,19 @@ class rcube_db /** * Return SQL function for current time and date * + * @param int $interval Optional interval (in seconds) to add/subtract + * * @return string SQL function to use in query */ - public function now() + public function now($interval = 0) { - return "now()"; + if ($interval) { + $add = ' ' . ($interval > 0 ? '+' : '-') . ' INTERVAL '; + $add .= $interval > 0 ? intval($interval) : intval($interval) * -1; + $add .= ' SECOND'; + } + + return "now()" . $add; } /** @@ -795,12 +778,19 @@ class rcube_db /** * Encodes non-UTF-8 characters in string/array/object (recursive) * - * @param mixed $input Data to fix + * @param mixed $input Data to fix + * @param bool $serialized Enable serialization * * @return mixed Properly UTF-8 encoded data */ - public static function encode($input) + public static function encode($input, $serialized = false) { + // use Base64 encoding to workaround issues with invalid + // or null characters in serialized string (#1489142) + if ($serialized) { + return base64_encode(serialize($input)); + } + if (is_object($input)) { foreach (get_object_vars($input) as $idx => $value) { $input->$idx = self::encode($value); @@ -811,6 +801,7 @@ class rcube_db foreach ($input as $idx => $value) { $input[$idx] = self::encode($value); } + return $input; } @@ -820,12 +811,24 @@ class rcube_db /** * Decodes encoded UTF-8 string/object/array (recursive) * - * @param mixed $input Input data + * @param mixed $input Input data + * @param bool $serialized Enable serialization * * @return mixed Decoded data */ - public static function decode($input) + public static function decode($input, $serialized = false) { + // use Base64 encoding to workaround issues with invalid + // or null characters in serialized string (#1489142) + if ($serialized) { + // Keep backward compatybility where base64 wasn't used + if (strpos(substr($input, 0, 16), ':') !== false) { + return self::decode(@unserialize($input)); + } + + return @unserialize(base64_decode($input)); + } + if (is_object($input)) { foreach (get_object_vars($input) as $idx => $value) { $input->$idx = self::decode($value); @@ -862,6 +865,17 @@ class rcube_db } /** + * Set class option value + * + * @param string $name Option name + * @param mixed $value Option value + */ + public function set_option($name, $value) + { + $this->options[$name] = $value; + } + + /** * MDB2 DSN string parser * * @param string $sequence Secuence name diff --git a/program/lib/Roundcube/rcube_db_mssql.php b/program/lib/Roundcube/rcube_db_mssql.php index 37a42678a..3c1b9d71f 100644 --- a/program/lib/Roundcube/rcube_db_mssql.php +++ b/program/lib/Roundcube/rcube_db_mssql.php @@ -29,38 +29,52 @@ class rcube_db_mssql extends rcube_db public $db_provider = 'mssql'; /** - * Driver initialization + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { + parent::__construct($db_dsnw, $db_dsnr, $pconn); + $this->options['identifier_start'] = '['; $this->options['identifier_end'] = ']'; } /** - * Character setting + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler */ - protected function set_charset($charset) + protected function conn_configure($dsn, $dbh) { - // UTF-8 is default + // Set date format in case of non-default language (#1488918) + $this->query("SET DATEFORMAT ymd"); } /** * Return SQL function for current time and date * + * @param int $interval Optional interval (in seconds) to add/subtract + * * @return string SQL function to use in query */ - public function now() + public function now($interval = 0) { + if ($interval) { + $interval = intval($interval); + return "dateadd(second, $interval, getdate())"; + } + return "getdate()"; } /** * Return SQL statement to convert a field value into a unix timestamp * - * This method is deprecated and should not be used anymore due to limitations - * of timestamp functions in Mysql (year 2038 problem) - * * @param string $field Field name * * @return string SQL statement to use in query diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index 2d42610b6..6fa5ad768 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -30,9 +30,13 @@ class rcube_db_mysql extends rcube_db public $db_provider = 'mysql'; /** - * Driver initialization/configuration + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { if (version_compare(PHP_VERSION, '5.3.0', '<')) { rcube::raise_error(array('code' => 600, 'type' => 'db', @@ -41,12 +45,25 @@ class rcube_db_mysql extends rcube_db true, true); } + parent::__construct($db_dsnw, $db_dsnr, $pconn); + // SQL identifiers quoting $this->options['identifier_start'] = '`'; $this->options['identifier_end'] = '`'; } /** + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler + */ + protected function conn_configure($dsn, $dbh) + { + $this->query("SET NAMES 'utf8'"); + } + + /** * Abstract SQL statement for value concatenation * * @return string SQL statement to be used in query diff --git a/program/lib/Roundcube/rcube_db_pgsql.php b/program/lib/Roundcube/rcube_db_pgsql.php index adfd2207b..d72c9d6b3 100644 --- a/program/lib/Roundcube/rcube_db_pgsql.php +++ b/program/lib/Roundcube/rcube_db_pgsql.php @@ -29,6 +29,17 @@ class rcube_db_pgsql extends rcube_db public $db_provider = 'postgres'; /** + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler + */ + protected function conn_configure($dsn, $dbh) + { + $this->query("SET NAMES 'utf8'"); + } + + /** * Get last inserted record ID * * @param string $table Table name (to find the incremented sequence) @@ -75,9 +86,6 @@ class rcube_db_pgsql extends rcube_db /** * Return SQL statement to convert a field value into a unix timestamp * - * This method is deprecated and should not be used anymore due to limitations - * of timestamp functions in Mysql (year 2038 problem) - * * @param string $field Field name * * @return string SQL statement to use in query @@ -89,6 +97,24 @@ class rcube_db_pgsql extends rcube_db } /** + * Return SQL function for current time and date + * + * @param int $interval Optional interval (in seconds) to add/subtract + * + * @return string SQL function to use in query + */ + public function now($interval = 0) + { + if ($interval) { + $add = ' ' . ($interval > 0 ? '+' : '-') . " interval '"; + $add .= $interval > 0 ? intval($interval) : intval($interval) * -1; + $add .= " seconds'"; + } + + return "now()" . $add; + } + + /** * Return SQL statement for case insensitive LIKE * * @param string $column Field name @@ -130,4 +156,38 @@ class rcube_db_pgsql extends rcube_db return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; } + /** + * Returns PDO DSN string from DSN array + * + * @param array $dsn DSN parameters + * + * @return string DSN string + */ + protected function dsn_string($dsn) + { + $params = array(); + $result = 'pgsql:'; + + if ($dsn['hostspec']) { + $params[] = 'host=' . $dsn['hostspec']; + } + else if ($dsn['socket']) { + $params[] = 'host=' . $dsn['socket']; + } + + if ($dsn['port']) { + $params[] = 'port=' . $dsn['port']; + } + + if ($dsn['database']) { + $params[] = 'dbname=' . $dsn['database']; + } + + if (!empty($params)) { + $result .= implode(';', $params); + } + + return $result; + } + } diff --git a/program/lib/Roundcube/rcube_db_sqlite.php b/program/lib/Roundcube/rcube_db_sqlite.php index 145b8a371..b66c56097 100644 --- a/program/lib/Roundcube/rcube_db_sqlite.php +++ b/program/lib/Roundcube/rcube_db_sqlite.php @@ -29,13 +29,6 @@ class rcube_db_sqlite extends rcube_db public $db_provider = 'sqlite'; /** - * Database character set - */ - protected function set_charset($charset) - { - } - - /** * Prepare connection */ protected function conn_prepare($dsn) @@ -56,10 +49,6 @@ class rcube_db_sqlite extends rcube_db */ protected function conn_configure($dsn, $dbh) { - // we emulate via callback some missing functions - $dbh->sqliteCreateFunction('unix_timestamp', array('rcube_db_sqlite', 'sqlite_unix_timestamp'), 1); - $dbh->sqliteCreateFunction('now', array('rcube_db_sqlite', 'sqlite_now'), 0); - // Initialize database structure in file is empty if (!empty($dsn['database']) && !filesize($dsn['database'])) { $data = file_get_contents(RCUBE_INSTALL_PATH . 'SQL/sqlite.initial.sql'); @@ -83,30 +72,32 @@ class rcube_db_sqlite extends rcube_db } /** - * Callback for sqlite: unix_timestamp() + * Return SQL statement to convert a field value into a unix timestamp + * + * @param string $field Field name + * + * @return string SQL statement to use in query + * @deprecated */ - public static function sqlite_unix_timestamp($timestamp = '') + public function unixtimestamp($field) { - $timestamp = trim($timestamp); - if (!$timestamp) { - $ret = time(); - } - else if (!preg_match('/^[0-9]+$/s', $timestamp)) { - $ret = strtotime($timestamp); - } - else { - $ret = $timestamp; - } - - return $ret; + return "strftime('%s', $field)"; } /** - * Callback for sqlite: now() + * Return SQL function for current time and date + * + * @param int $interval Optional interval (in seconds) to add/subtract + * + * @return string SQL function to use in query */ - public static function sqlite_now() + public function now($interval = 0) { - return date("Y-m-d H:i:s"); + if ($interval) { + $add = ($interval > 0 ? '+' : '') . intval($interval) . ' seconds'; + } + + return "datetime('now'" . ($add ? ",'$add'" : "") . ")"; } /** diff --git a/program/lib/Roundcube/rcube_db_sqlsrv.php b/program/lib/Roundcube/rcube_db_sqlsrv.php index e5dfb1154..45c41cdaf 100644 --- a/program/lib/Roundcube/rcube_db_sqlsrv.php +++ b/program/lib/Roundcube/rcube_db_sqlsrv.php @@ -29,29 +29,46 @@ class rcube_db_sqlsrv extends rcube_db public $db_provider = 'mssql'; /** - * Driver initialization + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { + parent::__construct($db_dsnw, $db_dsnr, $pconn); + $this->options['identifier_start'] = '['; $this->options['identifier_end'] = ']'; } /** - * Database character set setting + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler */ - protected function set_charset($charset) + protected function conn_configure($dsn, $dbh) { - // UTF-8 is default + // Set date format in case of non-default language (#1488918) + $this->query("SET DATEFORMAT ymd"); } /** * Return SQL function for current time and date * + * @param int $interval Optional interval (in seconds) to add/subtract + * * @return string SQL function to use in query */ - public function now() + public function now($interval = 0) { + if ($interval) { + $interval = intval($interval); + return "dateadd(second, $interval, getdate())"; + } + return "getdate()"; } diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php index 735a0df01..09bb4e81b 100644 --- a/program/lib/Roundcube/rcube_image.php +++ b/program/lib/Roundcube/rcube_image.php @@ -93,6 +93,10 @@ class rcube_image $convert = $rcube->config->get('im_convert_path', false); $props = $this->props(); + if (empty($props)) { + return false; + } + if (!$filename) { $filename = $this->image_file; } @@ -148,6 +152,10 @@ class rcube_image return false; } + if ($image === false) { + return false; + } + $scale = $size / max($props['width'], $props['height']); // Imagemagick resize is implemented in shrinking mode (see -resize argument above) diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 31e7079fb..39057a89a 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -812,20 +812,22 @@ class rcube_imap extends rcube_storage return $mcache->get_thread($folder); } - if (empty($this->icache['threads'])) { - if (!$this->check_connection()) { - return new rcube_result_thread(); + if (!empty($this->icache['threads'])) { + if ($this->icache['threads']->get_parameters('MAILBOX') == $folder) { + return $this->icache['threads']; } + } - // get all threads - $result = $this->conn->thread($folder, $this->threading, - $this->options['skip_deleted'] ? 'UNDELETED' : '', true); - - // add to internal (fast) cache - $this->icache['threads'] = $result; + if (!$this->check_connection()) { + return new rcube_result_thread(); } - return $this->icache['threads']; + // get all threads + $result = $this->conn->thread($folder, $this->threading, + $this->options['skip_deleted'] ? 'UNDELETED' : '', true); + + // add to internal (fast) cache + return $this->icache['threads'] = $result; } @@ -3691,7 +3693,7 @@ class rcube_imap extends rcube_storage { if ($this->caching && !$this->cache) { $rcube = rcube::get_instance(); - $ttl = $rcube->config->get('message_cache_lifetime', '10d'); + $ttl = $rcube->config->get('imap_cache_ttl', '10d'); $this->cache = $rcube->get_cache('IMAP', $this->caching, $ttl); } @@ -3739,24 +3741,6 @@ class rcube_imap extends rcube_storage } } - /** - * Delete outdated cache entries - */ - public function expunge_cache() - { - if ($this->mcache) { - $ttl = rcube::get_instance()->config->get('message_cache_lifetime', '10d'); - $this->mcache->expunge($ttl); - } - -/* - // this cache is expunged by rcube class - if ($this->cache) { - $this->cache->expunge(); - } -*/ - } - /* -------------------------------- * message caching methods @@ -3790,8 +3774,9 @@ class rcube_imap extends rcube_storage if ($this->messages_caching && !$this->mcache) { $rcube = rcube::get_instance(); if (($dbh = $rcube->get_dbh()) && ($userid = $rcube->get_user_id())) { + $ttl = $rcube->config->get('messages_cache_ttl', '10d'); $this->mcache = new rcube_imap_cache( - $dbh, $this, $userid, $this->options['skip_deleted']); + $dbh, $this, $userid, $this->options['skip_deleted'], $ttl); } } @@ -3813,6 +3798,15 @@ class rcube_imap extends rcube_storage } + /** + * Delete outdated cache entries + */ + function cache_gc() + { + rcube_imap_cache::gc(); + } + + /* -------------------------------- * protected methods * --------------------------------*/ diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php index 47d9aaf4a..e2fd2a98b 100644 --- a/program/lib/Roundcube/rcube_imap_cache.php +++ b/program/lib/Roundcube/rcube_imap_cache.php @@ -49,6 +49,13 @@ class rcube_imap_cache private $userid; /** + * Expiration time in seconds + * + * @var int + */ + private $ttl; + + /** * Internal (in-memory) cache * * @var array @@ -83,13 +90,25 @@ class rcube_imap_cache /** * Object constructor. + * + * @param rcube_db $db DB handler + * @param rcube_imap $imap IMAP handler + * @param int $userid User identifier + * @param bool $skip_deleted skip_deleted flag + * @param string $ttl Expiration time of memcache/apc items + * */ - function __construct($db, $imap, $userid, $skip_deleted) + function __construct($db, $imap, $userid, $skip_deleted, $ttl=0) { + // convert ttl string to seconds + $ttl = get_offset_sec($ttl); + if ($ttl > 2592000) $ttl = 2592000; + $this->db = $db; $this->imap = $imap; $this->userid = $userid; $this->skip_deleted = $skip_deleted; + $this->ttl = $ttl; } @@ -407,8 +426,8 @@ class rcube_imap_cache return; } - $msg = serialize($this->db->encode(clone $message)); $flags = 0; + $msg = clone $message; if (!empty($message->flags)) { foreach ($this->flags as $idx => $flag) { @@ -417,14 +436,16 @@ class rcube_imap_cache } } } + unset($msg->flags); + $msg = $this->db->encode($msg, true); // update cache record (even if it exists, the update // here will work as select, assume row exist if affected_rows=0) if (!$force) { $res = $this->db->query( "UPDATE ".$this->db->table_name('cache_messages') - ." SET flags = ?, data = ?, changed = ".$this->db->now() + ." SET flags = ?, data = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') ." WHERE user_id = ?" ." AND mailbox = ?" ." AND uid = ?", @@ -435,12 +456,29 @@ class rcube_imap_cache } } + $this->db->set_option('ignore_key_errors', true); + // insert new record - $this->db->query( + $res = $this->db->query( "INSERT INTO ".$this->db->table_name('cache_messages') - ." (user_id, mailbox, uid, flags, changed, data)" - ." VALUES (?, ?, ?, ?, ".$this->db->now().", ?)", + ." (user_id, mailbox, uid, flags, expires, data)" + ." VALUES (?, ?, ?, ?, ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?)", $this->userid, $mailbox, (int) $message->uid, $flags, $msg); + + // race-condition, insert failed so try update (#1489146) + // thanks to ignore_key_errors "duplicate row" errors will be ignored + if ($force && !$res && !$this->db->is_error($res)) { + $this->db->query( + "UPDATE ".$this->db->table_name('cache_messages') + ." SET expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') + .", flags = ?, data = ?" + ." WHERE user_id = ?" + ." AND mailbox = ?" + ." AND uid = ?", + $flags, $msg, $this->userid, $mailbox, (int) $message->uid); + } + + $this->db->set_option('ignore_key_errors', false); } @@ -481,7 +519,7 @@ class rcube_imap_cache $this->db->query( "UPDATE ".$this->db->table_name('cache_messages') - ." SET changed = ".$this->db->now() + ." SET expires = ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') .", flags = flags ".($enabled ? "+ $idx" : "- $idx") ." WHERE user_id = ?" ." AND mailbox = ?" @@ -604,23 +642,21 @@ class rcube_imap_cache /** - * Delete cache entries older than TTL - * - * @param string $ttl Lifetime of message cache entries + * Delete expired cache entries */ - function expunge($ttl) + static function gc() { - // get expiration timestamp - $ts = get_offset_time($ttl, -1); + $rcube = rcube::get_instance(); + $db = $rcube->get_dbh(); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_messages') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_messages') + ." WHERE expired < " . $db->now()); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_index') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_index') + ." WHERE expired < " . $db->now()); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_thread') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_thread') + ." WHERE expired < " . $db->now()); } @@ -639,7 +675,7 @@ class rcube_imap_cache if ($sql_arr = $this->db->fetch_assoc($sql_result)) { $data = explode('@', $sql_arr['data']); - $index = @unserialize($data[0]); + $index = $this->db->decode($data[0], true); unset($data[0]); if (empty($index)) { @@ -676,7 +712,7 @@ class rcube_imap_cache if ($sql_arr = $this->db->fetch_assoc($sql_result)) { $data = explode('@', $sql_arr['data']); - $thread = @unserialize($data[0]); + $thread = $this->db->decode($data[0], true); unset($data[0]); if (empty($thread)) { @@ -702,7 +738,7 @@ class rcube_imap_cache $data, $mbox_data = array(), $exists = false, $modseq = null) { $data = array( - serialize($data), + $this->db->encode($data, true), $sort_field, (int) $this->skip_deleted, (int) $mbox_data['UIDVALIDITY'], @@ -712,20 +748,38 @@ class rcube_imap_cache $data = implode('@', $data); if ($exists) { - $sql_result = $this->db->query( + $res = $this->db->query( "UPDATE ".$this->db->table_name('cache_index') - ." SET data = ?, valid = 1, changed = ".$this->db->now() + ." SET data = ?, valid = 1, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') ." WHERE user_id = ?" ." AND mailbox = ?", $data, $this->userid, $mailbox); + + if ($this->db->affected_rows($res)) { + return; + } } - else { - $sql_result = $this->db->query( - "INSERT INTO ".$this->db->table_name('cache_index') - ." (user_id, mailbox, data, valid, changed)" - ." VALUES (?, ?, ?, 1, ".$this->db->now().")", - $this->userid, $mailbox, $data); + + $this->db->set_option('ignore_key_errors', true); + + $res = $this->db->query( + "INSERT INTO ".$this->db->table_name('cache_index') + ." (user_id, mailbox, valid, expires, data)" + ." VALUES (?, ?, 1, ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') .", ?)", + $this->userid, $mailbox, $data); + + // race-condition, insert failed so try update (#1489146) + // thanks to ignore_key_errors "duplicate row" errors will be ignored + if (!$exists && !$res && !$this->db->is_error($res)) { + $res = $this->db->query( + "UPDATE ".$this->db->table_name('cache_index') + ." SET data = ?, valid = 1, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') + ." WHERE user_id = ?" + ." AND mailbox = ?", + $data, $this->userid, $mailbox); } + + $this->db->set_option('ignore_key_errors', false); } @@ -735,28 +789,48 @@ class rcube_imap_cache private function add_thread_row($mailbox, $data, $mbox_data = array(), $exists = false) { $data = array( - serialize($data), + $this->db->encode($data, true), (int) $this->skip_deleted, (int) $mbox_data['UIDVALIDITY'], (int) $mbox_data['UIDNEXT'], ); $data = implode('@', $data); + $expires = ($this->ttl ? $this->db->now($this->ttl) : 'NULL'); + if ($exists) { - $sql_result = $this->db->query( + $res = $this->db->query( "UPDATE ".$this->db->table_name('cache_thread') - ." SET data = ?, changed = ".$this->db->now() + ." SET data = ?, expires = $expires" ." WHERE user_id = ?" ." AND mailbox = ?", $data, $this->userid, $mailbox); + + if ($this->db->affected_rows($res)) { + return; + } } - else { - $sql_result = $this->db->query( - "INSERT INTO ".$this->db->table_name('cache_thread') - ." (user_id, mailbox, data, changed)" - ." VALUES (?, ?, ?, ".$this->db->now().")", - $this->userid, $mailbox, $data); + + $this->db->set_option('ignore_key_errors', true); + + $res = $this->db->query( + "INSERT INTO ".$this->db->table_name('cache_thread') + ." (user_id, mailbox, expires, data)" + ." VALUES (?, ?, $expires, ?)", + $this->userid, $mailbox, $data); + + // race-condition, insert failed so try update (#1489146) + // thanks to ignore_key_errors "duplicate row" errors will be ignored + if (!$exists && !$res && !$this->db->is_error($res)) { + $this->db->query( + "UPDATE ".$this->db->table_name('cache_thread') + ." SET expires = $expires, data = ?" + ." WHERE user_id = ?" + ." AND mailbox = ?", + $data, $this->userid, $mailbox); } + + $this->db->set_option('ignore_key_errors', false); } @@ -1004,7 +1078,7 @@ class rcube_imap_cache $this->db->query( "UPDATE ".$this->db->table_name('cache_messages') - ." SET flags = ?, changed = ".$this->db->now() + ." SET flags = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') ." WHERE user_id = ?" ." AND mailbox = ?" ." AND uid = ?" @@ -1067,7 +1141,7 @@ class rcube_imap_cache */ private function build_message($sql_arr) { - $message = $this->db->decode(unserialize($sql_arr['data'])); + $message = $this->db->decode($sql_arr['data'], true); if ($message) { $message->flags = array(); diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index 70163b21c..39a48b456 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -185,8 +185,12 @@ class rcube_ldap extends rcube_addressbook $this->mail_domain = $mail_domain; // initialize cache - $rcube = rcube::get_instance(); - $this->cache = $rcube->get_cache('LDAP.' . asciiwords($this->prop['name']), 'db', 600); + $rcube = rcube::get_instance(); + $cache_type = $rcube->config->get('ldap_cache', 'db'); + $cache_ttl = $rcube->config->get('ldap_cache_ttl', '10m'); + $cache_name = 'LDAP.' . asciiwords($this->prop['name']); + + $this->cache = $rcube->get_cache($cache_name, $cache_type, $cache_ttl); $this->_connect(); } diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index 5258af5bf..572540f47 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -587,23 +587,20 @@ class rcube_mime */ public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null, $wrap_quoted=true) { - if (!$charset) { - $charset = RCUBE_CHARSET; - } + // Note: Never try to use iconv instead of mbstring functions here + // Iconv's substr/strlen are 100x slower (#1489113) - // detect available functions - $strlen_func = function_exists('iconv_strlen') ? 'iconv_strlen' : 'mb_strlen'; - $strpos_func = function_exists('iconv_strpos') ? 'iconv_strpos' : 'mb_strpos'; - $strrpos_func = function_exists('iconv_strrpos') ? 'iconv_strrpos' : 'mb_strrpos'; - $substr_func = function_exists('iconv_substr') ? 'iconv_substr' : 'mb_substr'; + if ($charset && $charset != RCUBE_CHARSET && function_exists('mb_internal_encoding')) { + mb_internal_encoding($charset); + } // Convert \r\n to \n, this is our line-separator $string = str_replace("\r\n", "\n", $string); $separator = "\n"; // must be 1 character length $result = array(); - while (($stringLength = $strlen_func($string, $charset)) > 0) { - $breakPos = $strpos_func($string, $separator, 0, $charset); + while (($stringLength = mb_strlen($string)) > 0) { + $breakPos = mb_strpos($string, $separator, 0); // quoted line (do not wrap) if ($wrap_quoted && $string[0] == '>') { @@ -612,7 +609,7 @@ class rcube_mime $cutLength = null; } else { - $subString = $substr_func($string, 0, $breakPos, $charset); + $subString = mb_substr($string, 0, $breakPos); $cutLength = $breakPos + 1; } } @@ -623,39 +620,34 @@ class rcube_mime $cutLength = null; } else { - $subString = $substr_func($string, 0, $breakPos, $charset); + $subString = mb_substr($string, 0, $breakPos); $cutLength = $breakPos + 1; } } else { - $subString = $substr_func($string, 0, $width, $charset); + $subString = mb_substr($string, 0, $width); // last line if ($breakPos === false && $subString === $string) { $cutLength = null; } else { - $nextChar = $substr_func($string, $width, 1, $charset); + $nextChar = mb_substr($string, $width, 1); if ($nextChar === ' ' || $nextChar === $separator) { - $afterNextChar = $substr_func($string, $width + 1, 1, $charset); + $afterNextChar = mb_substr($string, $width + 1, 1); if ($afterNextChar === false) { $subString .= $nextChar; } - $cutLength = $strlen_func($subString, $charset) + 1; + $cutLength = mb_strlen($subString) + 1; } else { - if ($strrpos_func[0] == 'm') { - $spacePos = $strrpos_func($subString, ' ', 0, $charset); - } - else { - $spacePos = $strrpos_func($subString, ' ', $charset); - } + $spacePos = mb_strrpos($subString, ' ', 0); if ($spacePos !== false) { - $subString = $substr_func($subString, 0, $spacePos, $charset); + $subString = mb_substr($subString, 0, $spacePos); $cutLength = $spacePos + 1; } else if ($cut === false && $breakPos === false) { @@ -663,19 +655,19 @@ class rcube_mime $cutLength = null; } else if ($cut === false) { - $spacePos = $strpos_func($string, ' ', 0, $charset); + $spacePos = mb_strpos($string, ' ', 0); if ($spacePos !== false && $spacePos < $breakPos) { - $subString = $substr_func($string, 0, $spacePos, $charset); + $subString = mb_substr($string, 0, $spacePos); $cutLength = $spacePos + 1; } else { - $subString = $substr_func($string, 0, $breakPos, $charset); + $subString = mb_substr($string, 0, $breakPos); $cutLength = $breakPos + 1; } } else { - $subString = $substr_func($subString, 0, $width, $charset); + $subString = mb_substr($subString, 0, $width); $cutLength = $width; } } @@ -685,13 +677,17 @@ class rcube_mime $result[] = $subString; if ($cutLength !== null) { - $string = $substr_func($string, $cutLength, ($stringLength - $cutLength), $charset); + $string = mb_substr($string, $cutLength, ($stringLength - $cutLength)); } else { break; } } + if ($charset && $charset != RCUBE_CHARSET && function_exists('mb_internal_encoding')) { + mb_internal_encoding(RCUBE_CHARSET); + } + return implode($break, $result); } diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index dedde2284..4e0682749 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -42,6 +42,7 @@ class rcube_session private $secret = ''; private $ip_check = false; private $logging = false; + private $storage; private $memcache; @@ -59,11 +60,14 @@ class rcube_session $this->set_lifetime($lifetime); // use memcache backend - if ($config->get('session_storage', 'db') == 'memcache') { + $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'), @@ -79,7 +83,9 @@ class rcube_session true, true); } } - else { + 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'), @@ -92,6 +98,22 @@ class rcube_session } + /** + * Wrapper for session_start() + */ + 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; @@ -116,6 +138,20 @@ class rcube_session /** + * Wrapper for session_write_close() + */ + public function write_close() + { + if ($this->storage == 'php') { + $_SESSION['__IP'] = $this->ip; + $_SESSION['__MTIME'] = time(); + } + + session_write_close(); + } + + + /** * Read session data from database * * @param string Session ID diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index 700d12ffb..b17291bdf 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -986,6 +986,6 @@ abstract class rcube_storage /** * Delete outdated cache entries */ - abstract function expunge_cache(); + abstract function cache_gc(); } // end class rcube_storage diff --git a/program/lib/Roundcube/rcube_user.php b/program/lib/Roundcube/rcube_user.php index 505b190d1..5e9c9af80 100644 --- a/program/lib/Roundcube/rcube_user.php +++ b/program/lib/Roundcube/rcube_user.php @@ -495,9 +495,9 @@ class rcube_user "INSERT INTO ".$dbh->table_name('users'). " (created, last_login, username, mail_host, language)". " VALUES (".$dbh->now().", ".$dbh->now().", ?, ?, ?)", - strip_newlines($data['user']), - strip_newlines($data['host']), - strip_newlines($data['language'])); + $data['user'], + $data['host'], + $data['language']); if ($user_id = $dbh->insert_id('users')) { // create rcube_user instance to make plugin hooks work @@ -517,7 +517,7 @@ class rcube_user if (empty($user_email)) { $user_email = strpos($data['user'], '@') ? $user : sprintf('%s@%s', $data['user'], $mail_domain); } - $email_list[] = strip_newlines($user_email); + $email_list[] = $user_email; } // identities_level check else if (count($email_list) > 1 && $rcube->config->get('identities_level', 0) > 1) { @@ -547,7 +547,6 @@ class rcube_user $record['name'] = $user_name != $record['email'] ? $user_name : ''; } - $record['name'] = strip_newlines($record['name']); $record['user_id'] = $user_id; $record['standard'] = $standard; diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index 8467107fe..29baa82f3 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -510,17 +510,24 @@ class rcube_utils */ public static function file2class($mimetype, $filename) { + $mimetype = strtolower($mimetype); + $filename = strtolower($filename); + list($primary, $secondary) = explode('/', $mimetype); $classes = array($primary ? $primary : 'unknown'); + if ($secondary) { $classes[] = $secondary; } - if (preg_match('/\.([a-z0-9]+)$/i', $filename, $m)) { - $classes[] = $m[1]; + + if (preg_match('/\.([a-z0-9]+)$/', $filename, $m)) { + if (!in_array($m[1], $classes)) { + $classes[] = $m[1]; + } } - return strtolower(join(" ", $classes)); + return join(" ", $classes); } @@ -726,7 +733,7 @@ class rcube_utils return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); } else if (is_numeric($date)) { - return $date; + return (int) $date; } // Clean malformed data @@ -755,7 +762,7 @@ class rcube_utils $date = implode(' ', $d); } - return $ts; + return (int) $ts; } diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php index a11371c61..6b2efcc78 100644 --- a/program/lib/Roundcube/rcube_washtml.php +++ b/program/lib/Roundcube/rcube_washtml.php @@ -113,10 +113,9 @@ class rcube_washtml 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value' ); - /* Block elements which could be empty but cannot be returned in short form (<tag />) */ - static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center', - 'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', - 'i', 'b', 'u', 'span', + /* Elements which could be empty and be returned in short form (<tag />) */ + static $void_elements = array('area', 'base', 'br', 'col', 'command', 'embed', 'hr', + 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' ); /* State for linked objects in HTML */ @@ -134,8 +133,8 @@ class rcube_washtml /* Ignore these HTML tags but process their content */ private $_ignore_elements = array(); - /* Block elements which could be empty but cannot be returned in short form (<tag />) */ - private $_block_elements = array(); + /* Elements which could be empty and be returned in short form (<tag />) */ + private $_void_elements = array(); /* Allowed HTML attributes */ private $_html_attribs = array(); @@ -152,9 +151,9 @@ class rcube_washtml $this->_html_elements = array_flip((array)$p['html_elements']) + array_flip(self::$html_elements) ; $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); - $this->_block_elements = array_flip((array)$p['block_elements']) + array_flip(self::$block_elements); + $this->_void_elements = array_flip((array)$p['void_elements']) + array_flip(self::$void_elements); - unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['block_elements']); + unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['void_elements']); $this->config = $p + array('show_washed' => true, 'allow_remote' => false, 'cid_map' => array()); } @@ -321,7 +320,7 @@ class rcube_washtml else if (isset($this->_html_elements[$tagName])) { $content = $this->dumpHtml($node, $level); $dump .= '<' . $tagName . $this->wash_attribs($node) . - ($content != '' || isset($this->_block_elements[$tagName]) ? ">$content</$tagName>" : ' />'); + ($content === '' && isset($this->_void_elements[$tagName]) ? ' />' : ">$content</$tagName>"); } else if (isset($this->_ignore_elements[$tagName])) { $dump .= '<!-- ' . htmlspecialchars($tagName, ENT_QUOTES) . ' not allowed -->'; |