summaryrefslogtreecommitdiff
path: root/program/lib
diff options
context:
space:
mode:
Diffstat (limited to 'program/lib')
-rw-r--r--program/lib/Roundcube/bootstrap.php26
-rw-r--r--program/lib/Roundcube/rcube.php53
-rw-r--r--program/lib/Roundcube/rcube_cache.php76
-rw-r--r--program/lib/Roundcube/rcube_cache_shared.php57
-rw-r--r--program/lib/Roundcube/rcube_config.php23
-rw-r--r--program/lib/Roundcube/rcube_db.php118
-rw-r--r--program/lib/Roundcube/rcube_db_mssql.php32
-rw-r--r--program/lib/Roundcube/rcube_db_mysql.php21
-rw-r--r--program/lib/Roundcube/rcube_db_pgsql.php66
-rw-r--r--program/lib/Roundcube/rcube_db_sqlite.php47
-rw-r--r--program/lib/Roundcube/rcube_db_sqlsrv.php29
-rw-r--r--program/lib/Roundcube/rcube_image.php8
-rw-r--r--program/lib/Roundcube/rcube_imap.php54
-rw-r--r--program/lib/Roundcube/rcube_imap_cache.php156
-rw-r--r--program/lib/Roundcube/rcube_ldap.php8
-rw-r--r--program/lib/Roundcube/rcube_mime.php52
-rw-r--r--program/lib/Roundcube/rcube_session.php40
-rw-r--r--program/lib/Roundcube/rcube_storage.php2
-rw-r--r--program/lib/Roundcube/rcube_user.php9
-rw-r--r--program/lib/Roundcube/rcube_utils.php17
-rw-r--r--program/lib/Roundcube/rcube_washtml.php17
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 -->';