From 6ddb16d181e285d4f0ef0ef55bdd0ba787f1b583 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli <thomas@roundcube.net> Date: Sat, 17 Nov 2012 10:24:09 -0500 Subject: [PATCH] Merge branch 'master' of github.com:roundcube/roundcubemail --- program/include/rcube_imap.php | 673 +++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 501 insertions(+), 172 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 3e2abe7..9054b6b 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -19,19 +19,16 @@ | Author: Thomas Bruederli <roundcube@gmail.com> | | Author: Aleksander Machniak <alec@alec.pl> | +-----------------------------------------------------------------------+ - - $Id$ - */ /** * Interface class for accessing an IMAP server * - * @package Mail + * @package Framework + * @subpackage Storage * @author Thomas Bruederli <roundcube@gmail.com> * @author Aleksander Machniak <alec@alec.pl> - * @version 2.0 */ class rcube_imap extends rcube_storage { @@ -132,7 +129,7 @@ $this->options['ssl_mode'] = $use_ssl == 'imaps' ? 'ssl' : $use_ssl; } else if ($use_ssl) { - raise_error(array('code' => 403, 'type' => 'imap', + rcube::raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__, 'line' => __LINE__, 'message' => "OpenSSL not available"), true, false); $port = 143; @@ -154,7 +151,7 @@ $attempt = 0; do { - $data = rcmail::get_instance()->plugins->exec_hook('imap_connect', + $data = rcube::get_instance()->plugins->exec_hook('imap_connect', array_merge($this->options, array('host' => $host, 'user' => $user, 'attempt' => ++$attempt))); @@ -185,9 +182,9 @@ else if ($this->conn->error) { if ($pass && $user) { $message = sprintf("Login failed for %s from %s. %s", - $user, rcmail_remote_ip(), $this->conn->error); + $user, rcube_utils::remote_ip(), $this->conn->error); - raise_error(array('code' => 403, 'type' => 'imap', + rcube::raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__, 'line' => __LINE__, 'message' => $message), true, false); } @@ -212,6 +209,8 @@ /** * Check connection state, connect if not connected. + * + * @return bool Connection state. */ public function check_connection() { @@ -360,11 +359,11 @@ return array( $this->search_string, - $this->search_set, - $this->search_charset, - $this->search_sort_field, - $this->search_sorted, - ); + $this->search_set, + $this->search_charset, + $this->search_sort_field, + $this->search_sorted, + ); } @@ -402,15 +401,56 @@ */ public function check_permflag($flag) { - $flag = strtoupper($flag); - $imap_flag = $this->conn->flags[$flag]; + $flag = strtoupper($flag); + $imap_flag = $this->conn->flags[$flag]; + $perm_flags = $this->get_permflags($this->folder); - if ($this->folder !== null) { - $this->check_connection(); + return in_array_nocase($imap_flag, $perm_flags); + } + + + /** + * Returns PERMANENTFLAGS of the specified folder + * + * @param string $folder Folder name + * + * @return array Flags + */ + public function get_permflags($folder) + { + if (!strlen($folder)) { + return array(); } - // @TODO: cache permanent flags (?) +/* + Checking PERMANENTFLAGS is rather rare, so we disable caching of it + Re-think when we'll use it for more than only MDNSENT flag - return (in_array_nocase($imap_flag, $this->conn->data['PERMANENTFLAGS'])); + $cache_key = 'mailboxes.permanentflags.' . $folder; + $permflags = $this->get_cache($cache_key); + + if ($permflags !== null) { + return explode(' ', $permflags); + } +*/ + if (!$this->check_connection()) { + return array(); + } + + if ($this->conn->select($folder)) { + $permflags = $this->conn->data['PERMANENTFLAGS']; + } + else { + return array(); + } + + if (!is_array($permflags)) { + $permflags = array(); + } +/* + // Store permflags as string to limit cached object size + $this->update_cache($cache_key, implode(' ', $permflags)); +*/ + return $permflags; } @@ -455,7 +495,7 @@ return; } - $config = rcmail::get_instance()->config; + $config = rcube::get_instance()->config; $imap_personal = $config->get('imap_ns_personal'); $imap_other = $config->get('imap_ns_other'); $imap_shared = $config->get('imap_ns_shared'); @@ -544,7 +584,7 @@ $folder = $this->folder; } - return $this->messagecount($folder, $mode, $force, $status); + return $this->countmessages($folder, $mode, $force, $status); } @@ -560,12 +600,12 @@ * @return int Number of messages * @see rcube_imap::count() */ - protected function messagecount($folder, $mode='ALL', $force=false, $status=true) + protected function countmessages($folder, $mode='ALL', $force=false, $status=true) { $mode = strtoupper($mode); - // count search set - if ($this->search_string && $folder == $this->folder && ($mode == 'ALL' || $mode == 'THREADS') && !$force) { + // count search set, assume search set is always up-to-date (don't check $force flag) + if ($this->search_string && $folder == $this->folder && ($mode == 'ALL' || $mode == 'THREADS')) { if ($mode == 'ALL') { return $this->search_set->count_messages(); } @@ -832,8 +872,8 @@ * protected method for setting threaded messages flags: * depth, has_children and unread_children * - * @param array $headers Reference to headers array indexed by message UID - * @param rcube_imap_result $threads Threads data object + * @param array $headers Reference to headers array indexed by message UID + * @param rcube_result_thread $threads Threads data object * * @return array Message headers array indexed by message UID */ @@ -972,8 +1012,8 @@ $a_msg_headers, $this->sort_field, $this->sort_order); // only return the requested part of the set - $a_msg_headers = array_slice(array_values($a_msg_headers), - $from, min($cnt-$to, $this->page_size)); + $slice_length = min($this->page_size, $cnt - ($to > $cnt ? $from : $to)); + $a_msg_headers = array_slice(array_values($a_msg_headers), $from, $slice_length); if ($slice) { $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice); @@ -1046,7 +1086,7 @@ if ($sort) { // use this class for message sorting - $sorter = new rcube_header_sorter(); + $sorter = new rcube_message_header_sorter(); $sorter->set_index($msgs); $sorter->sort_headers($a_msg_headers); } @@ -1073,7 +1113,7 @@ $old = $this->get_folder_stats($folder); // refresh message count -> will update - $this->messagecount($folder, 'ALL', true); + $this->countmessages($folder, 'ALL', true); $result = 0; @@ -1211,7 +1251,9 @@ } // use message index sort as default sorting else if (!$sort_field) { + // use search result from count() if possible if ($this->options['skip_deleted'] && !empty($this->icache['undeleted_idx']) + && $this->icache['undeleted_idx']->get_parameters('ALL') !== null && $this->icache['undeleted_idx']->get_parameters('MAILBOX') == $folder ) { $index = $this->icache['undeleted_idx']; @@ -1344,21 +1386,21 @@ * * @return rcube_result_index Search result (UIDs) */ - public function search_once($mailbox = null, $str = 'ALL') + public function search_once($folder = null, $str = 'ALL') { if (!$str) { return 'ALL'; } - if (!strlen($mailbox)) { - $mailbox = $this->mailbox; + if (!strlen($folder)) { + $folder = $this->folder; } if (!$this->check_connection()) { return new rcube_result_index(); } - $index = $this->conn->search($mailbox, $str, true); + $index = $this->conn->search($folder, $str, true); return $index; } @@ -1392,6 +1434,12 @@ $criteria = 'UNDELETED '.$criteria; } + // unset CHARSET if criteria string is ASCII, this way + // SEARCH won't be re-sent after "unsupported charset" response + if ($charset && $charset != 'US-ASCII' && is_ascii($criteria)) { + $charset = 'US-ASCII'; + } + if ($this->threading) { $threads = $this->conn->thread($folder, $this->threading, $criteria, true, $charset); @@ -1423,7 +1471,7 @@ } $messages = $this->conn->search($folder, - ($charset ? "CHARSET $charset " : '') . $criteria, true); + ($charset && $charset != 'US-ASCII' ? "CHARSET $charset " : '') . $criteria, true); // Error, try with US-ASCII (some servers may support only US-ASCII) if ($messages->is_error() && $charset && $charset != 'US-ASCII') { @@ -1454,7 +1502,7 @@ foreach ($matches[1] as $m) { $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n $string = substr($str, $string_offset - 1, $m[0]); - $string = rcube_charset_convert($string, $charset, $dest_charset); + $string = rcube_charset::convert($string, $charset, $dest_charset); if ($string === false) { continue; } @@ -1496,7 +1544,7 @@ * @param string $folder Folder to read from * @param bool $force True to skip cache * - * @return rcube_mail_header Message headers + * @return rcube_message_header Message headers */ public function get_message_headers($uid, $folder = null, $force = false) { @@ -1527,7 +1575,7 @@ * @param int $uid Message UID to fetch * @param string $folder Folder to read from * - * @return object rcube_mail_header Message data + * @return object rcube_message_header Message data */ public function get_message($uid, $folder = null) { @@ -1593,11 +1641,24 @@ $structure[1] = $m[2]; } else { - return $headers; + // Try to parse the message using Mail_mimeDecode package + // We need a better solution, Mail_mimeDecode parses message + // in memory, which wouldn't work for very big messages, + // (it uses up to 10x more memory than the message size) + // it's also buggy and not actively developed + if ($headers->size && rcube_utils::mem_check($headers->size * 10)) { + $raw_msg = $this->get_raw_body($uid); + $struct = rcube_mime::parse_message($raw_msg); + } + else { + return $headers; + } } } - $struct = $this->structure_part($structure, 0, '', $headers); + if (empty($struct)) { + $struct = $this->structure_part($structure, 0, '', $headers); + } // don't trust given content-type if (empty($struct->parts) && !empty($headers->ctype)) { @@ -1946,7 +2007,7 @@ $charset = $this->struct_charset; } else { - $charset = rc_detect_encoding($filename_mime, $this->default_charset); + $charset = rcube_charset::detect($filename_mime, $this->default_charset); } $part->filename = rcube_mime::decode_mime_string($filename_mime, $charset); @@ -1958,7 +2019,7 @@ $filename_encoded = $fmatches[2]; } - $part->filename = rcube_charset_convert(urldecode($filename_encoded), $filename_charset); + $part->filename = rcube_charset::convert(urldecode($filename_encoded), $filename_charset); } } @@ -2013,7 +2074,7 @@ if ($o_part && $o_part->size) { $body = $this->conn->handlePartBody($this->folder, $uid, true, - $part ? $part : 'TEXT', $o_part->encoding, $print, $fp); + $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $o_part->ctype_primary == 'text'); } if ($fp || $print) { @@ -2037,7 +2098,7 @@ $o_part->charset = $this->default_charset; } } - $body = rcube_charset_convert($body, $o_part->charset); + $body = rcube_charset::convert($body, $o_part->charset); } } @@ -2083,14 +2144,17 @@ /** * Sends the whole message source to stdout + * + * @param int $uid Message UID + * @param bool $formatted Enables line-ending formatting */ - public function print_raw_body($uid) + public function print_raw_body($uid, $formatted = true) { if (!$this->check_connection()) { return; } - $this->conn->handlePartBody($this->folder, $uid, true, NULL, NULL, true); + $this->conn->handlePartBody($this->folder, $uid, true, null, null, true, null, $formatted); } @@ -2155,23 +2219,33 @@ * @param string $message The message source string or filename * @param string $headers Headers string if $message contains only the body * @param boolean $is_file True if $message is a filename + * @param array $flags Message flags + * @param mixed $date Message internal date * * @return int|bool Appended message UID or True on success, False on error */ - public function save_message($folder, &$message, $headers='', $is_file=false) + public function save_message($folder, &$message, $headers='', $is_file=false, $flags = array(), $date = null) { if (!strlen($folder)) { $folder = $this->folder; } + if (!$this->check_connection()) { + return false; + } + // make sure folder exists - if ($this->folder_exists($folder)) { - if ($is_file) { - $saved = $this->conn->appendFromFile($folder, $message, $headers); - } - else { - $saved = $this->conn->append($folder, $message); - } + if (!$this->folder_exists($folder)) { + return false; + } + + $date = $this->date_format($date); + + if ($is_file) { + $saved = $this->conn->appendFromFile($folder, $message, $headers, $flags, $date); + } + else { + $saved = $this->conn->append($folder, $message, $flags, $date); } if ($saved) { @@ -2225,7 +2299,7 @@ } } - $config = rcmail::get_instance()->config; + $config = rcube::get_instance()->config; $to_trash = $to_mbox == $config->get('trash_mbox'); // flag messages as read before moving them @@ -2464,7 +2538,16 @@ return $a_mboxes; } - $a_mboxes = $this->_list_folders_subscribed($root, $name, $filter, $rights); + // Give plugins a chance to provide a list of folders + $data = rcube::get_instance()->plugins->exec_hook('storage_folders', + array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LSUB')); + + if (isset($data['folders'])) { + $a_mboxes = $data['folders']; + } + else { + $a_mboxes = $this->list_folders_subscribed_direct($root, $name); + } if (!is_array($a_mboxes)) { return array(); @@ -2493,74 +2576,74 @@ /** - * protected method for folders listing (LSUB) + * Method for direct folders listing (LSUB) * * @param string $root Optional root folder * @param string $name Optional name pattern - * @param mixed $filter Optional filter - * @param string $rights Optional ACL requirements * * @return array List of subscribed folders * @see rcube_imap::list_folders_subscribed() */ - protected function _list_folders_subscribed($root='', $name='*', $filter=null, $rights=null) + public function list_folders_subscribed_direct($root='', $name='*') { - $a_defaults = $a_out = array(); - - // Give plugins a chance to provide a list of folders - $data = rcmail::get_instance()->plugins->exec_hook('folders_list', - array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LSUB')); - - if (isset($data['folders'])) { - $a_folders = $data['folders']; - } - else if (!$this->check_connection()) { + if (!$this->check_connection()) { return null; } - else { - // Server supports LIST-EXTENDED, we can use selection options - $config = rcmail::get_instance()->config; - // #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED - if (!$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED')) { - // This will also set folder options, LSUB doesn't do that - $a_folders = $this->conn->listMailboxes($root, $name, - NULL, array('SUBSCRIBED')); - // unsubscribe non-existent folders, remove from the list - if (is_array($a_folders) && $name == '*') { - foreach ($a_folders as $idx => $folder) { - if ($this->conn->data['LIST'] && ($opts = $this->conn->data['LIST'][$folder]) - && in_array('\\NonExistent', $opts) - ) { + $config = rcube::get_instance()->config; + + // Server supports LIST-EXTENDED, we can use selection options + // #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED + $list_extended = !$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED'); + if ($list_extended) { + // This will also set folder options, LSUB doesn't do that + $a_folders = $this->conn->listMailboxes($root, $name, + NULL, array('SUBSCRIBED')); + } + else { + // retrieve list of folders from IMAP server using LSUB + $a_folders = $this->conn->listSubscribed($root, $name); + } + + if (!is_array($a_folders)) { + return array(); + } + + // #1486796: some server configurations doesn't return folders in all namespaces + if ($root == '' && $name == '*' && $config->get('imap_force_ns')) { + $this->list_folders_update($a_folders, ($list_extended ? 'ext-' : '') . 'subscribed'); + } + + if ($list_extended) { + // unsubscribe non-existent folders, remove from the list + // we can do this only when LIST response is available + if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) { + foreach ($a_folders as $idx => $folder) { + if (($opts = $this->conn->data['LIST'][$folder]) + && in_array('\\NonExistent', $opts) + ) { + $this->conn->unsubscribe($folder); + unset($a_folders[$idx]); + } + } + } + } + else { + // unsubscribe non-existent folders, remove them from the list, + // we can do this only when LIST response is available + if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) { + foreach ($a_folders as $idx => $folder) { + if (!isset($this->conn->data['LIST'][$folder]) + || in_array('\\Noselect', $this->conn->data['LIST'][$folder]) + ) { + // Some servers returns \Noselect for existing folders + if (!$this->folder_exists($folder)) { $this->conn->unsubscribe($folder); unset($a_folders[$idx]); } } } } - // retrieve list of folders from IMAP server using LSUB - else { - $a_folders = $this->conn->listSubscribed($root, $name); - - // unsubscribe non-existent folders, remove from the list - if (is_array($a_folders) && $name == '*') { - foreach ($a_folders as $idx => $folder) { - if ($this->conn->data['LIST'] && ($opts = $this->conn->data['LIST'][$folder]) - && in_array('\\Noselect', $opts) - ) { - // Some servers returns \Noselect for existing folders - if (!$this->folder_exists($folder)) { - $this->conn->unsubscribe($folder); - unset($a_folders[$idx]); - } - } - } - } - } - } - - if (!is_array($a_folders) || !sizeof($a_folders)) { - $a_folders = array(); } return $a_folders; @@ -2594,7 +2677,7 @@ } // Give plugins a chance to provide a list of folders - $data = rcmail::get_instance()->plugins->exec_hook('folders_list', + $data = rcube::get_instance()->plugins->exec_hook('storage_folders', array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST')); if (isset($data['folders'])) { @@ -2602,7 +2685,7 @@ } else { // retrieve list of folders from IMAP server - $a_mboxes = $this->_list_folders($root, $name); + $a_mboxes = $this->list_folders_direct($root, $name); } if (!is_array($a_mboxes)) { @@ -2637,7 +2720,7 @@ /** - * protected method for folders listing (LIST) + * Method for direct folders listing (LIST) * * @param string $root Optional root folder * @param string $name Optional name pattern @@ -2645,7 +2728,7 @@ * @return array List of folders * @see rcube_imap::list_folders() */ - protected function _list_folders($root='', $name='*') + public function list_folders_direct($root='', $name='*') { if (!$this->check_connection()) { return null; @@ -2657,50 +2740,71 @@ return array(); } - // #1486796: some server configurations doesn't - // return folders in all namespaces, we'll try to detect that situation - // and ask for these namespaces separately - if ($root == '' && $name == '*') { - $delim = $this->get_hierarchy_delimiter(); - $namespace = $this->get_namespace(); - $search = array(); + $config = rcube::get_instance()->config; - // build list of namespace prefixes - foreach ((array)$namespace as $ns) { - if (is_array($ns)) { - foreach ($ns as $ns_data) { - if (strlen($ns_data[0])) { - $search[] = $ns_data[0]; - } - } - } - } + // #1486796: some server configurations doesn't return folders in all namespaces + if ($root == '' && $name == '*' && $config->get('imap_force_ns')) { + $this->list_folders_update($result); + } - if (!empty($search)) { - // go through all folders detecting namespace usage - foreach ($result as $folder) { - foreach ($search as $idx => $prefix) { - if (strpos($folder, $prefix) === 0) { - unset($search[$idx]); - } - } - if (empty($search)) { - break; - } - } + return $result; + } - // get folders in hidden namespaces and add to the result - foreach ($search as $prefix) { - $list = $this->conn->listMailboxes($prefix, $name); - if (!empty($list)) { - $result = array_merge($result, $list); + /** + * Fix folders list by adding folders from other namespaces. + * Needed on some servers eg. Courier IMAP + * + * @param array $result Reference to folders list + * @param string $type Listing type (ext-subscribed, subscribed or all) + */ + private function list_folders_update(&$result, $type = null) + { + $delim = $this->get_hierarchy_delimiter(); + $namespace = $this->get_namespace(); + $search = array(); + + // build list of namespace prefixes + foreach ((array)$namespace as $ns) { + if (is_array($ns)) { + foreach ($ns as $ns_data) { + if (strlen($ns_data[0])) { + $search[] = $ns_data[0]; } } } } - return $result; + if (!empty($search)) { + // go through all folders detecting namespace usage + foreach ($result as $folder) { + foreach ($search as $idx => $prefix) { + if (strpos($folder, $prefix) === 0) { + unset($search[$idx]); + } + } + if (empty($search)) { + break; + } + } + + // get folders in hidden namespaces and add to the result + foreach ($search as $prefix) { + if ($type == 'ext-subscribed') { + $list = $this->conn->listMailboxes('', $prefix . '*', null, array('SUBSCRIBED')); + } + else if ($type == 'subscribed') { + $list = $this->conn->listSubscribed('', $prefix . '*'); + } + else { + $list = $this->conn->listMailboxes('', $prefix . '*'); + } + + if (!empty($list)) { + $result = array_merge($result, $list); + } + } + } } @@ -2729,7 +2833,7 @@ */ public function get_quota() { - if ($this->get_capability('QUOTA')) { + if ($this->get_capability('QUOTA') && $this->check_connection()) { return $this->conn->getQuota(); } @@ -2841,11 +2945,11 @@ // get list of subscribed folders if ((strpos($folder, '%') === false) && (strpos($folder, '*') === false)) { - $a_subscribed = $this->_list_folders_subscribed('', $folder . $delm . '*'); + $a_subscribed = $this->list_folders_subscribed('', $folder . $delm . '*'); $subscribed = $this->folder_exists($folder, true); } else { - $a_subscribed = $this->_list_folders_subscribed(); + $a_subscribed = $this->list_folders_subscribed(); $subscribed = in_array($folder, $a_subscribed); } @@ -2913,7 +3017,7 @@ if (strpos($c_mbox, $folder.$delm) === 0) { $this->conn->unsubscribe($c_mbox); if ($this->conn->deleteFolder($c_mbox)) { - $this->clear_message_cache($c_mbox); + $this->clear_message_cache($c_mbox); } } } @@ -3140,6 +3244,14 @@ return $this->icache['options']; } + // get cached metadata + $cache_key = 'mailboxes.folder-info.' . $folder; + $cached = $this->get_cache($cache_key); + + if (is_array($cached)) { + return $cached; + } + $acl = $this->get_capability('ACL'); $namespace = $this->get_namespace(); $options = array(); @@ -3175,10 +3287,9 @@ $options['name'] = $folder; $options['attributes'] = $this->folder_attributes($folder, true); $options['namespace'] = $this->folder_namespace($folder); - $options['rights'] = $acl && !$options['is_root'] ? (array)$this->my_rights($folder) : array(); $options['special'] = in_array($folder, $this->default_folders); - // Set 'noselect' and 'norename' flags + // Set 'noselect' flag if (is_array($options['attributes'])) { foreach ($options['attributes'] as $attrib) { $attrib = strtolower($attrib); @@ -3191,6 +3302,12 @@ $options['noselect'] = true; } + // Get folder rights (MYRIGHTS) + if ($acl && ($rights = $this->my_rights($folder))) { + $options['rights'] = $rights; + } + + // Set 'norename' flag if (!empty($options['rights'])) { $options['norename'] = !in_array('x', $options['rights']) && !in_array('d', $options['rights']); @@ -3202,7 +3319,9 @@ $options['norename'] = $options['is_root'] || $options['namespace'] != 'personal'; } + // update caches $this->icache['options'] = $options; + $this->update_cache($cache_key, $options); return $options; } @@ -3267,6 +3386,8 @@ if (!$this->check_connection()) { return false; } + + $this->clear_cache('mailboxes.folder-info.' . $folder); return $this->conn->setACL($folder, $user, $acl); } @@ -3381,6 +3502,8 @@ return false; } + $this->clear_cache('mailboxes.metadata.', true); + if ($this->get_capability('METADATA') || (!strlen($folder) && $this->get_capability('METADATA-SERVER')) ) { @@ -3413,7 +3536,9 @@ return false; } - if ($this->get_capability('METADATA') || + $this->clear_cache('mailboxes.metadata.', true); + + if ($this->get_capability('METADATA') || (!strlen($folder) && $this->get_capability('METADATA-SERVER')) ) { return $this->conn->deleteMetadata($folder, $entries); @@ -3442,6 +3567,23 @@ */ public function get_metadata($folder, $entries, $options=array()) { + $entries = (array)$entries; + + // create cache key + // @TODO: this is the simplest solution, but we do the same with folders list + // maybe we should store data per-entry and merge on request + sort($options); + sort($entries); + $cache_key = 'mailboxes.metadata.' . $folder; + $cache_key .= '.' . md5(serialize($options).serialize($entries)); + + // get cached data + $cached_data = $this->get_cache($cache_key); + + if (is_array($cached_data)) { + return $cached_data; + } + if (!$this->check_connection()) { return null; } @@ -3449,14 +3591,14 @@ if ($this->get_capability('METADATA') || (!strlen($folder) && $this->get_capability('METADATA-SERVER')) ) { - return $this->conn->getMetadata($folder, $entries, $options); + $res = $this->conn->getMetadata($folder, $entries, $options); } else if ($this->get_capability('ANNOTATEMORE') || $this->get_capability('ANNOTATEMORE2')) { $queries = array(); $res = array(); // Convert entry names - foreach ((array)$entries as $entry) { + foreach ($entries as $entry) { list($ent, $attr) = $this->md2annotate($entry); $queries[$attr][] = $ent; } @@ -3467,7 +3609,10 @@ $res = array_merge_recursive($res, $result); } } + } + if (isset($res)) { + $this->update_cache($cache_key, $res); return $res; } @@ -3504,7 +3649,7 @@ /** * Enable or disable indexes caching * - * @param string $type Cache type (@see rcmail::get_cache) + * @param string $type Cache type (@see rcube::get_cache) */ public function set_caching($type) { @@ -3526,8 +3671,9 @@ protected function get_cache_engine() { if ($this->caching && !$this->cache) { - $rcmail = rcmail::get_instance(); - $this->cache = $rcmail->get_cache('IMAP', $this->caching); + $rcube = rcube::get_instance(); + $ttl = $rcube->config->get('message_cache_lifetime', '10d'); + $this->cache = $rcube->get_cache('IMAP', $this->caching, $ttl); } return $this->cache; @@ -3553,7 +3699,7 @@ * @param string $key Cache key * @param mixed $data Data */ - protected function update_cache($key, $data) + public function update_cache($key, $data) { if ($cache = $this->get_cache_engine()) { $cache->set($key, $data); @@ -3571,6 +3717,21 @@ { if ($cache = $this->get_cache_engine()) { $cache->remove($key, $prefix_mode); + } + } + + /** + * 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); + } + + if ($this->cache) { + $this->cache->expunge(); } } @@ -3605,10 +3766,10 @@ protected function get_mcache_engine() { if ($this->messages_caching && !$this->mcache) { - $rcmail = rcmail::get_instance(); - if ($dbh = $rcmail->get_dbh()) { + $rcube = rcube::get_instance(); + if (($dbh = $rcube->get_dbh()) && ($userid = $rcube->get_user_id())) { $this->mcache = new rcube_imap_cache( - $dbh, $this, $rcmail->user->ID, $this->options['skip_deleted']); + $dbh, $this, $userid, $this->options['skip_deleted']); } } @@ -3672,7 +3833,7 @@ $a_defaults[$p] = $folder; } else { - $folders[$folder] = rcube_charset_convert($folder, 'UTF7-IMAP'); + $folders[$folder] = rcube_charset::convert($folder, 'UTF7-IMAP'); } } @@ -3702,12 +3863,12 @@ protected function rsort($folder, $delimiter, &$list, &$out) { while (list($key, $name) = each($list)) { - if (strpos($name, $folder.$delimiter) === 0) { - // set the type of folder name variable (#1485527) - $out[] = (string) $name; - unset($list[$key]); - $this->rsort($name, $delimiter, $list, $out); - } + if (strpos($name, $folder.$delimiter) === 0) { + // set the type of folder name variable (#1485527) + $out[] = (string) $name; + unset($list[$key]); + $this->rsort($name, $delimiter, $list, $out); + } } reset($list); } @@ -3827,12 +3988,180 @@ /** + * Converts date string/object into IMAP date/time format + */ + protected function date_format($date) + { + if (empty($date)) { + return null; + } + + if (!is_object($date) || !is_a($date, 'DateTime')) { + try { + $timestamp = rcube_utils::strtotime($date); + $date = new DateTime("@".$timestamp); + } + catch (Exception $e) { + return null; + } + } + + return $date->format('d-M-Y H:i:s O'); + } + + + /** * This is our own debug handler for the IMAP connection * @access public */ public function debug_handler(&$imap, $message) { - write_log('imap', $message); + rcube::write_log('imap', $message); } -} // end class rcube_imap + + /** + * Deprecated methods (to be removed) + */ + + public function decode_address_list($input, $max = null, $decode = true, $fallback = null) + { + return rcube_mime::decode_address_list($input, $max, $decode, $fallback); + } + + public function decode_header($input, $fallback = null) + { + return rcube_mime::decode_mime_string((string)$input, $fallback); + } + + public static function decode_mime_string($input, $fallback = null) + { + return rcube_mime::decode_mime_string($input, $fallback); + } + + public function mime_decode($input, $encoding = '7bit') + { + return rcube_mime::decode($input, $encoding); + } + + public static function explode_header_string($separator, $str, $remove_comments = false) + { + return rcube_mime::explode_header_string($separator, $str, $remove_comments); + } + + public function select_mailbox($mailbox) + { + // do nothing + } + + public function set_mailbox($folder) + { + $this->set_folder($folder); + } + + public function get_mailbox_name() + { + return $this->get_folder(); + } + + public function list_headers($folder='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) + { + return $this->list_messages($folder, $page, $sort_field, $sort_order, $slice); + } + + public function get_headers($uid, $folder = null, $force = false) + { + return $this->get_message_headers($uid, $folder, $force); + } + + public function mailbox_status($folder = null) + { + return $this->folder_status($folder); + } + + public function message_index($folder = '', $sort_field = NULL, $sort_order = NULL) + { + return $this->index($folder, $sort_field, $sort_order); + } + + public function message_index_direct($folder, $sort_field = null, $sort_order = null, $skip_cache = true) + { + return $this->index_direct($folder, $sort_field, $sort_order, $skip_cache); + } + + public function list_mailboxes($root='', $name='*', $filter=null, $rights=null, $skip_sort=false) + { + return $this->list_folders_subscribed($root, $name, $filter, $rights, $skip_sort); + } + + public function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false) + { + return $this->list_folders($root, $name, $filter, $rights, $skip_sort); + } + + public function get_mailbox_size($folder) + { + return $this->folder_size($folder); + } + + public function create_mailbox($folder, $subscribe=false) + { + return $this->create_folder($folder, $subscribe); + } + + public function rename_mailbox($folder, $new_name) + { + return $this->rename_folder($folder, $new_name); + } + + function delete_mailbox($folder) + { + return $this->delete_folder($folder); + } + + function clear_mailbox($folder = null) + { + return $this->clear_folder($folder); + } + + public function mailbox_exists($folder, $subscription=false) + { + return $this->folder_exists($folder, $subscription); + } + + public function mailbox_namespace($folder) + { + return $this->folder_namespace($folder); + } + + public function mod_mailbox($folder, $mode = 'out') + { + return $this->mod_folder($folder, $mode); + } + + public function mailbox_attributes($folder, $force=false) + { + return $this->folder_attributes($folder, $force); + } + + public function mailbox_data($folder) + { + return $this->folder_data($folder); + } + + public function mailbox_info($folder) + { + return $this->folder_info($folder); + } + + public function mailbox_sync($folder) + { + return $this->folder_sync($folder); + } + + public function expunge($folder='', $clear_cache=true) + { + return $this->expunge_folder($folder, $clear_cache); + } + +} -- Gitblit v1.9.1