diff --git a/CHANGELOG b/CHANGELOG index ee5960b15..dc74f81ce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix bug where serialized strings were truncated in PDO::quote() (#1489142) - Fix displaying messages with invalid self-closing HTML tags (#1489137) - Fix PHP warning when responding to a message with many Return-Path headers (#1489136) - Fix unintentional compose window resize (#1489114) diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php index 92f12a8bf..deaba68e9 100644 --- a/program/lib/Roundcube/rcube_cache.php +++ b/program/lib/Roundcube/rcube_cache.php @@ -145,7 +145,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)); } @@ -219,7 +219,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 +255,7 @@ class rcube_cache if ($data) { $md5sum = md5($data); - $data = $this->packed ? unserialize($data) : $data; + $data = $this->unserialize($data); if ($nostore) { return $data; @@ -283,7 +283,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) { @@ -364,7 +364,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) { @@ -553,4 +552,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_db.php b/program/lib/Roundcube/rcube_db.php index 2f0a32ad6..adfe3ca3c 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -789,12 +789,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); @@ -805,6 +812,7 @@ class rcube_db foreach ($input as $idx => $value) { $input[$idx] = self::encode($value); } + return $input; } @@ -814,12 +822,19 @@ 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) { + if ($serialized) { + // use Base64 encoding to workaround issues with invalid + // or null characters in serialized string (#1489142) + return @unserialize(base64_decode($input)); + } + if (is_object($input)) { foreach (get_object_vars($input) as $idx => $value) { $input->$idx = self::decode($value); diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php index 89e3ceb6f..a3bdf2196 100644 --- a/program/lib/Roundcube/rcube_imap_cache.php +++ b/program/lib/Roundcube/rcube_imap_cache.php @@ -419,7 +419,7 @@ class rcube_imap_cache } unset($msg->flags); - $msg = serialize($this->db->encode($msg)); + $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) @@ -641,7 +641,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)) { @@ -678,7 +678,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)) { @@ -704,7 +704,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'], @@ -737,7 +737,7 @@ 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'], @@ -1069,7 +1069,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();