From b550c2eaa5a36d460d859bdf10b5bd88ff2d7ebe Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Thu, 25 Jun 2009 15:45:39 -0400 Subject: [PATCH] - fix rcube_message_part cloning --- program/include/rcube_imap.php | 234 ++++++++++++++++++++++++++++++---------------------------- 1 files changed, 122 insertions(+), 112 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 76aa853..8ffcfe0 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -118,7 +118,7 @@ $this->port = $port; $this->ssl = $use_ssl; - // print trace mesages + // print trace messages if ($this->conn && ($this->debug_level & 8)) console($this->conn->message); @@ -772,14 +772,14 @@ */ function _fetch_headers($mailbox, $msgs, &$a_msg_headers, $cache_key) { - // cache is incomplete - $cache_index = $this->get_message_cache_index($cache_key); - // fetch reqested headers from server $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs, false, $this->fetch_add_headers); - + if (!empty($a_header_index)) { + // cache is incomplete + $cache_index = $this->get_message_cache_index($cache_key); + foreach ($a_header_index as $i => $headers) { /* @@ -794,12 +794,13 @@ */ // add message to cache if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid) - $this->add_message_cache($cache_key, $headers->id, $headers); + $this->add_message_cache($cache_key, $headers->id, $headers, NULL, + !in_array((string)$headers->uid, $cache_index, true)); $a_msg_headers[$headers->uid] = $headers; } } - + return count($a_msg_headers); } @@ -863,7 +864,6 @@ } // fetch complete message index - $msg_count = $this->_messagecount($mailbox); if ($this->get_capability('sort') && ($a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, $this->skip_deleted ? 'UNDELETED' : ''))) { if ($this->sort_order == 'DESC') @@ -873,7 +873,7 @@ } else { - $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", $this->sort_field, $this->skip_deleted); + $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:*", $this->sort_field, $this->skip_deleted); if ($this->sort_order=="ASC") asort($a_index); @@ -913,31 +913,32 @@ // message in cache but in wrong position if (in_array((string)$uid, $cache_index, TRUE)) { - unset($cache_index[$id]); + unset($cache_index[$id]); } // other message at this position if (isset($cache_index[$id])) { - $this->remove_message_cache($cache_key, $cache_index[$id]); + $for_remove[] = $cache_index[$id]; unset($cache_index[$id]); } - $toupdate[] = $id; + $for_update[] = $id; } - // fetch complete headers and add to cache - if (!empty($toupdate)) { - if ($headers = iil_C_FetchHeader($this->conn, $mailbox, join(',', $toupdate), false, $this->fetch_add_headers)) - foreach ($headers as $header) - $this->add_message_cache($cache_key, $header->id, $header); - } + // clear messages at wrong positions and those deleted that are still in cache_index + if (!empty($for_remove)) + $cache_index = array_merge($cache_index, $for_remove); - // those ids that are still in cache_index have been deleted if (!empty($cache_index)) - { - foreach ($cache_index as $id => $uid) - $this->remove_message_cache($cache_key, $uid); + $this->remove_message_cache($cache_key, $cache_index); + + // fetch complete headers and add to cache + if (!empty($for_update)) { + if ($headers = iil_C_FetchHeader($this->conn, $mailbox, join(',', $for_update), false, $this->fetch_add_headers)) + foreach ($headers as $header) + $this->add_message_cache($cache_key, $header->id, $header, NULL, + in_array((string)$header->uid, (array)$for_remove, true)); } } @@ -963,7 +964,7 @@ // try search with US-ASCII charset (should be supported by server) // only if UTF-8 search is not supported - if (empty($results) && !is_array($results) && !empty($charset) && $charset!='US-ASCII') + if (empty($results) && !is_array($results) && !empty($charset) && $charset != 'US-ASCII') { // convert strings to US_ASCII if(preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) @@ -984,7 +985,7 @@ else // strings for conversion not found $res = $str; - $results = $this->search($mbox_name, $res, 'US-ASCII', $sort_field); + $results = $this->search($mbox_name, $res, NULL, $sort_field); } $this->set_search_set($str, $results, $charset, $sort_field); @@ -1086,7 +1087,7 @@ if ($headers->uid && $headers->id) $this->uid_id_map[$mailbox][$headers->uid] = $headers->id; - $this->add_message_cache($mailbox.'.msg', $headers->id, $headers); + $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL, true); } return $headers; @@ -1104,7 +1105,7 @@ function &get_structure($uid, $structure_str='') { $cache_key = $this->mailbox.'.msg'; - $headers = &$this->get_cached_message($cache_key, $uid, true); + $headers = &$this->get_cached_message($cache_key, $uid); // return cached message structure if (is_object($headers) && is_object($headers->structure)) { @@ -1546,12 +1547,10 @@ $result = iil_C_Flag($this->conn, $mailbox, join(',', $uids), $flag); // reload message headers if cached - $cache_key = $mailbox.'.msg'; if ($this->caching_enabled) { - foreach ($uids as $uid) - if ($cached_headers = $this->get_cached_message($cache_key, $uid)) - $this->remove_message_cache($cache_key, $uid); + $cache_key = $mailbox.'.msg'; + $this->remove_message_cache($cache_key, $uids); // close and re-open connection // this prevents connection problems with Courier @@ -1650,15 +1649,7 @@ } // update cached message headers $cache_key = $from_mbox.'.msg'; - if ($moved && ($a_cache_index = $this->get_message_cache_index($cache_key))) - { - $start_index = 100000; - foreach ($a_uids as $uid) - { - if (($index = array_search($uid, $a_cache_index)) !== FALSE) - $start_index = min($index, $start_index); - } - + if ($moved && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { // clear cache from the lowest index on $this->clear_message_cache($cache_key, $start_index); } @@ -1705,15 +1696,7 @@ // remove deleted messages from cache $cache_key = $mailbox.'.msg'; - if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key))) - { - $start_index = 100000; - foreach ($a_uids as $uid) - { - if (($index = array_search($uid, $a_cache_index)) !== FALSE) - $start_index = min($index, $start_index); - } - + if ($deleted && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { // clear cache from the lowest index on $this->clear_message_cache($cache_key, $start_index); } @@ -1963,7 +1946,7 @@ /** * Remove mailboxes from server * - * @param string Mailbox name + * @param string Mailbox name(s) string/array * @return boolean True on success */ function delete_mailbox($mbox_name) @@ -1987,9 +1970,11 @@ // send delete command to server $result = iil_C_DeleteFolder($this->conn, $mailbox); - if ($result>=0) + if ($result >= 0) { $deleted = TRUE; - + $this->clear_message_cache($mailbox.'.msg'); + } + foreach ($all_mboxes as $c_mbox) { $regex = preg_quote($mailbox . $this->delimiter, '/'); @@ -1998,18 +1983,17 @@ { iil_C_UnSubscribe($this->conn, $c_mbox); $result = iil_C_DeleteFolder($this->conn, $c_mbox); - if ($result>=0) + if ($result >= 0) { $deleted = TRUE; - } + $this->clear_message_cache($c_mbox.'.msg'); + } + } } } // clear mailboxlist cache if ($deleted) - { - $this->clear_message_cache($mailbox.'.msg'); $this->clear_cache('mailboxes'); - } return $deleted; } @@ -2059,8 +2043,7 @@ // read cache if (!isset($this->cache[$key]) && $this->caching_enabled) { - $cache_data = $this->_read_cache_record('IMAP.'.$key); - $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE; + return $this->_read_cache_record($key); } return $this->cache[$key]; @@ -2086,7 +2069,7 @@ foreach ($this->cache as $key => $data) { if ($this->cache_changes[$key]) - $this->_write_cache_record('IMAP.'.$key, serialize($data)); + $this->_write_cache_record($key, serialize($data)); } } } @@ -2102,7 +2085,7 @@ if ($key===NULL) { foreach ($this->cache as $key => $data) - $this->_clear_cache_record('IMAP.'.$key); + $this->_clear_cache_record($key); $this->cache = array(); $this->cache_changed = FALSE; @@ -2110,7 +2093,7 @@ } else { - $this->_clear_cache_record('IMAP.'.$key); + $this->_clear_cache_record($key); $this->cache_changes[$key] = FALSE; unset($this->cache[$key]); } @@ -2121,27 +2104,25 @@ */ function _read_cache_record($key) { - $cache_data = FALSE; - if ($this->db) { // get cached data from DB $sql_result = $this->db->query( - "SELECT cache_id, data + "SELECT cache_id, data, cache_key FROM ".get_table_name('cache')." WHERE user_id=? - AND cache_key=?", - $_SESSION['user_id'], - $key); + AND cache_key LIKE 'IMAP.%'", + $_SESSION['user_id']); - if ($sql_arr = $this->db->fetch_assoc($sql_result)) + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $cache_data = $sql_arr['data']; - $this->cache_keys[$key] = $sql_arr['cache_id']; + $sql_key = preg_replace('/^IMAP\./', '', $sql_arr['cache_key']); + $this->cache_keys[$sql_key] = $sql_arr['cache_id']; + $this->cache[$sql_key] = $sql_arr['data'] ? unserialize($sql_arr['data']) : FALSE; } } - return $cache_data; + return $this->cache[$key]; } /** @@ -2161,7 +2142,7 @@ WHERE user_id=? AND cache_key=?", $_SESSION['user_id'], - $key); + 'IMAP.'.$key); if ($sql_arr = $this->db->fetch_assoc($sql_result)) $this->cache_keys[$key] = $sql_arr['cache_id']; @@ -2179,7 +2160,7 @@ AND cache_key=?", $data, $_SESSION['user_id'], - $key); + 'IMAP.'.$key); } // add new cache record else @@ -2189,7 +2170,7 @@ (created, user_id, cache_key, data) VALUES (".$this->db->now().", ?, ?, ?)", $_SESSION['user_id'], - $key, + 'IMAP.'.$key, $data); } } @@ -2204,7 +2185,7 @@ WHERE user_id=? AND cache_key=?", $_SESSION['user_id'], - $key); + 'IMAP.'.$key); } @@ -2221,7 +2202,7 @@ * @param string Internal cache key * @return int -3 = off, -2 = incomplete, -1 = dirty */ - function check_cache_status($mailbox, $cache_key) + private function check_cache_status($mailbox, $cache_key) { if (!$this->caching_enabled) return -3; @@ -2267,7 +2248,7 @@ /** * @access private */ - function get_message_cache($key, $from, $to, $sort_field, $sort_order) + private function get_message_cache($key, $from, $to, $sort_field, $sort_order) { $cache_key = "$key:$from:$to:$sort_field:$sort_order"; $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); @@ -2283,8 +2264,7 @@ FROM ".get_table_name('messages')." WHERE user_id=? AND cache_key=? - ORDER BY ".$this->db->quoteIdentifier($sort_field)." ". - strtoupper($sort_order), + ORDER BY ".$this->db->quoteIdentifier($sort_field)." ".strtoupper($sort_order), $from, $to-$from, $_SESSION['user_id'], @@ -2293,30 +2273,28 @@ while ($sql_arr = $this->db->fetch_assoc($sql_result)) { $uid = $sql_arr['uid']; - $this->cache[$cache_key][$uid] = unserialize($sql_arr['headers']); - + $this->cache[$cache_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); + // featch headers if unserialize failed if (empty($this->cache[$cache_key][$uid])) $this->cache[$cache_key][$uid] = iil_C_FetchHeader($this->conn, preg_replace('/.msg$/', '', $key), $uid, true, $this->fetch_add_headers); } } - + return $this->cache[$cache_key]; } /** * @access private */ - function &get_cached_message($key, $uid, $struct=false) + private function &get_cached_message($key, $uid) { $internal_key = '__single_msg'; - if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || - ($struct && empty($this->cache[$internal_key][$uid]->structure)))) + if ($this->caching_enabled && !isset($this->cache[$internal_key][$uid])) { - $sql_select = "idx, uid, headers" . ($struct ? ", structure" : ''); $sql_result = $this->db->query( - "SELECT $sql_select + "SELECT idx, headers, structure FROM ".get_table_name('messages')." WHERE user_id=? AND cache_key=? @@ -2327,9 +2305,10 @@ if ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $this->cache[$internal_key][$uid] = unserialize($sql_arr['headers']); + $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = $sql_arr['idx']; + $this->cache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); if (is_object($this->cache[$internal_key][$uid]) && !empty($sql_arr['structure'])) - $this->cache[$internal_key][$uid]->structure = unserialize($sql_arr['structure']); + $this->cache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); } } @@ -2339,7 +2318,7 @@ /** * @access private */ - function get_message_cache_index($key, $force=FALSE, $sort_field='idx', $sort_order='ASC') + private function get_message_cache_index($key, $force=FALSE, $sort_field='idx', $sort_order='ASC') { static $sa_message_index = array(); @@ -2369,21 +2348,22 @@ /** * @access private */ - function add_message_cache($key, $index, $headers, $struct=null) + private function add_message_cache($key, $index, $headers, $struct=null, $force=false) { if (empty($key) || !is_object($headers) || empty($headers->uid)) return; // add to internal (fast) cache - $this->cache['__single_msg'][$headers->uid] = $headers; + $this->cache['__single_msg'][$headers->uid] = clone $headers; $this->cache['__single_msg'][$headers->uid]->structure = $struct; - + // no further caching if (!$this->caching_enabled) return; // check for an existing record (probly headers are cached but structure not) - $sql_result = $this->db->query( + if (!$force) { + $sql_result = $this->db->query( "SELECT message_id FROM ".get_table_name('messages')." WHERE user_id=? @@ -2393,18 +2373,21 @@ $_SESSION['user_id'], $key, $headers->uid); + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + $message_id = $sql_arr['message_id']; + } // update cache record - if ($sql_arr = $this->db->fetch_assoc($sql_result)) + if ($message_id) { $this->db->query( "UPDATE ".get_table_name('messages')." SET idx=?, headers=?, structure=? WHERE message_id=?", $index, - serialize($headers), - is_object($struct) ? serialize($struct) : NULL, - $sql_arr['message_id'] + serialize($this->db->encode(clone $headers)), + is_object($struct) ? serialize($this->db->encode(clone $struct)) : NULL, + $message_id ); } else // insert new record @@ -2417,13 +2400,13 @@ $key, $index, $headers->uid, - (string)substr($this->decode_header($headers->subject, TRUE), 0, 128), - (string)substr($this->decode_header($headers->from, TRUE), 0, 128), - (string)substr($this->decode_header($headers->to, TRUE), 0, 128), - (string)substr($this->decode_header($headers->cc, TRUE), 0, 128), + (string)rc_substr($this->db->encode($this->decode_header($headers->subject, TRUE)), 0, 128), + (string)rc_substr($this->db->encode($this->decode_header($headers->from, TRUE)), 0, 128), + (string)rc_substr($this->db->encode($this->decode_header($headers->to, TRUE)), 0, 128), + (string)rc_substr($this->db->encode($this->decode_header($headers->cc, TRUE)), 0, 128), (int)$headers->size, - serialize($headers), - is_object($struct) ? serialize($struct) : NULL + serialize($this->db->encode(clone $headers)), + is_object($struct) ? serialize($this->db->encode(clone $struct)) : NULL ); } } @@ -2431,25 +2414,24 @@ /** * @access private */ - function remove_message_cache($key, $uid) + private function remove_message_cache($key, $uids) { if (!$this->caching_enabled) return; $this->db->query( "DELETE FROM ".get_table_name('messages')." - WHERE user_id=? - AND cache_key=? - AND uid=?", + WHERE user_id=? + AND cache_key=? + AND uid IN (".$this->db->array2list($uids, 'integer').")", $_SESSION['user_id'], - $key, - $uid); + $key); } /** * @access private */ - function clear_message_cache($key, $start_index=1) + private function clear_message_cache($key, $start_index=1) { if (!$this->caching_enabled) return; @@ -2464,7 +2446,28 @@ $start_index); } + /** + * @access private + */ + private function get_message_cache_index_min($key, $uids=NULL) + { + if (!$this->caching_enabled) + return; + + $sql_result = $this->db->query( + "SELECT MIN(idx) AS minidx + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=?" + .(!empty($uids) ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : ''), + $_SESSION['user_id'], + $key); + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + return $sql_arr['minidx']; + else + return 0; + } /* -------------------------------- @@ -2689,7 +2692,7 @@ /** - * Convert body charset to UTF-8 according to the ctype_parameters + * Convert body charset to RCMAIL_CHARSET according to the ctype_parameters * * @param string Part body to decode * @param string Charset to convert from @@ -3033,6 +3036,13 @@ var $d_parameters = array(); var $ctype_parameters = array(); + function __clone() + { + if (isset($this->parts)) + foreach ($this->parts as $idx => $part) + if (is_object($part)) + $this->parts[$idx] = clone $part; + } } -- Gitblit v1.9.1