From d61d668b64c44fc046095b807834c4836a8c05c5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Wed, 08 Apr 2015 02:57:21 -0400 Subject: [PATCH] Remove useless code --- program/lib/Roundcube/rcube_imap.php | 463 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 359 insertions(+), 104 deletions(-) diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 628338a..65e0950 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -56,6 +56,7 @@ */ protected $icache = array(); + protected $plugins; protected $list_page = 1; protected $delimiter; protected $namespace; @@ -82,6 +83,7 @@ public function __construct() { $this->conn = new rcube_imap_generic(); + $this->plugins = rcube::get_instance()->plugins; // Set namespace and delimiter from session, // so some methods would work before connection @@ -110,13 +112,13 @@ /** * Connect to an IMAP server * - * @param string $host Host to connect - * @param string $user Username for IMAP account - * @param string $pass Password for IMAP account - * @param integer $port Port to connect to - * @param string $use_ssl SSL schema (either ssl or tls) or null if plain connection + * @param string $host Host to connect + * @param string $user Username for IMAP account + * @param string $pass Password for IMAP account + * @param integer $port Port to connect to + * @param string $use_ssl SSL schema (either ssl or tls) or null if plain connection * - * @return boolean TRUE on success, FALSE on failure + * @return boolean True on success, False on failure */ public function connect($host, $user, $pass, $port=143, $use_ssl=null) { @@ -147,7 +149,7 @@ $attempt = 0; do { - $data = rcube::get_instance()->plugins->exec_hook('storage_connect', + $data = $this->plugins->exec_hook('storage_connect', array_merge($this->options, array('host' => $host, 'user' => $user, 'attempt' => ++$attempt))); @@ -170,8 +172,20 @@ $this->connect_done = true; if ($this->conn->connected()) { + // check for session identifier + $session = null; + if (preg_match('/\s+SESSIONID=([^=\s]+)/', $this->conn->result, $m)) { + $session = $m[1]; + } + // get namespace and delimiter $this->set_env(); + + // trigger post-connect hook + $this->plugins->exec_hook('storage_connected', array( + 'host' => $host, 'user' => $user, 'session' => $session + )); + return true; } // write error log @@ -332,6 +346,10 @@ $this->search_sort_field = $set[3]; $this->search_sorted = $set[4]; $this->search_threads = is_a($this->search_set, 'rcube_result_thread'); + + if (is_a($this->search_set, 'rcube_result_multifolder')) { + $this->set_threading(false); + } } @@ -757,7 +775,7 @@ $page = $page ? $page : $this->list_page; // use saved message set - if ($this->search_string && $folder == $this->folder) { + if ($this->search_string) { return $this->list_search_messages($folder, $page, $slice); } @@ -945,6 +963,75 @@ return array(); } + // gather messages from a multi-folder search + if ($this->search_set->multi) { + $page_size = $this->page_size; + $sort_field = $this->sort_field; + $search_set = $this->search_set; + + // prepare paging + $cnt = $search_set->count(); + $from = ($page-1) * $page_size; + $to = $from + $page_size; + $slice_length = min($page_size, $cnt - $from); + + // fetch resultset headers, sort and slice them + if (!empty($sort_field)) { + $this->sort_field = null; + $this->page_size = 1000; // fetch up to 1000 matching messages per folder + $this->threading = false; + + $a_msg_headers = array(); + foreach ($search_set->sets as $resultset) { + if (!$resultset->is_empty()) { + $this->search_set = $resultset; + $this->search_threads = $resultset instanceof rcube_result_thread; + $a_msg_headers = array_merge($a_msg_headers, $this->list_search_messages($resultset->get_parameters('MAILBOX'), 1)); + } + } + + // sort headers + if (!empty($a_msg_headers)) { + $a_msg_headers = $this->conn->sortHeaders($a_msg_headers, $sort_field, $this->sort_order); + } + + // store (sorted) message index + $search_set->set_message_index($a_msg_headers, $sort_field, $this->sort_order); + + // only return the requested part of the set + $a_msg_headers = array_slice(array_values($a_msg_headers), $from, $slice_length); + } + else { + if ($this->sort_order != $search_set->get_parameters('ORDER')) { + $search_set->revert(); + } + + // slice resultset first... + $fetch = array(); + foreach (array_slice($search_set->get(), $from, $slice_length) as $msg_id) { + list($uid, $folder) = explode('-', $msg_id, 2); + $fetch[$folder][] = $uid; + } + + // ... and fetch the requested set of headers + $a_msg_headers = array(); + foreach ($fetch as $folder => $a_index) { + $a_msg_headers = array_merge($a_msg_headers, array_values($this->fetch_headers($folder, $a_index))); + } + } + + if ($slice) { + $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice); + } + + // restore members + $this->sort_field = $sort_field; + $this->page_size = $page_size; + $this->search_set = $search_set; + + return $a_msg_headers; + } + // use saved messages from searching if ($this->threading) { return $this->list_search_thread_messages($folder, $page, $slice); @@ -1111,6 +1198,7 @@ } foreach ($headers as $h) { + $h->folder = $folder; $a_msg_headers[$h->uid] = $h; } @@ -1234,8 +1322,13 @@ return new rcube_result_index($folder, '* SORT'); } + if ($this->search_set instanceof rcube_result_multifolder) { + $index = $this->search_set; + $index->folder = $folder; + // TODO: handle changed sorting + } // search result is an index with the same sorting? - if (($this->search_set instanceof rcube_result_index) + else if (($this->search_set instanceof rcube_result_index) && ((!$this->sort_field && !$this->search_sorted) || ($this->search_sorted && $this->search_sort_field == $this->sort_field)) ) { @@ -1291,7 +1384,7 @@ public function index_direct($folder, $sort_field = null, $sort_order = null, $search = null) { if (!empty($search)) { - $search = $this->search_set->get_compressed(); + $search = $search->get_compressed(); } // use message index sort as default sorting @@ -1410,26 +1503,75 @@ * Invoke search request to IMAP server * * @param string $folder Folder name to search in - * @param string $str Search criteria + * @param string $search Search criteria * @param string $charset Search charset * @param string $sort_field Header field to sort by * + * @return rcube_result_index Search result object * @todo: Search criteria should be provided in non-IMAP format, eg. array */ - public function search($folder='', $str='ALL', $charset=NULL, $sort_field=NULL) + public function search($folder = '', $search = 'ALL', $charset = null, $sort_field = null) { - if (!$str) { - $str = 'ALL'; + if (!$search) { + $search = 'ALL'; } - if (!strlen($folder)) { + if ((is_array($folder) && empty($folder)) || (!is_array($folder) && !strlen($folder))) { $folder = $this->folder; } - $results = $this->search_index($folder, $str, $charset, $sort_field); + $plugin = $this->plugins->exec_hook('imap_search_before', array( + 'folder' => $folder, + 'search' => $search, + 'charset' => $charset, + 'sort_field' => $sort_field, + 'threading' => $this->threading, + )); - $this->set_search_set(array($str, $results, $charset, $sort_field, - $this->threading || $this->search_sorted ? true : false)); + $folder = $plugin['folder']; + $search = $plugin['search']; + $charset = $plugin['charset']; + $sort_field = $plugin['sort_field']; + $results = $plugin['result']; + + // multi-folder search + if (!$results && is_array($folder) && count($folder) > 1 && $search != 'ALL') { + // connect IMAP to have all the required classes and settings loaded + $this->check_connection(); + + // disable threading + $this->threading = false; + + $searcher = new rcube_imap_search($this->options, $this->conn); + + // set limit to not exceed the client's request timeout + $searcher->set_timelimit(60); + + // continue existing incomplete search + if (!empty($this->search_set) && $this->search_set->incomplete && $search == $this->search_string) { + $searcher->set_results($this->search_set); + } + + // execute the search + $results = $searcher->exec( + $folder, + $search, + $charset ? $charset : $this->default_charset, + $sort_field && $this->get_capability('SORT') ? $sort_field : null, + $this->threading + ); + } + else if (!$results) { + $folder = is_array($folder) ? $folder[0] : $folder; + $search = is_array($search) ? $search[$folder] : $search; + $results = $this->search_index($folder, $search, $charset, $sort_field); + } + + $sorted = $this->threading || $this->search_sorted || $plugin['search_sorted'] ? true : false; + + $this->set_search_set(array($search, $results, $charset, $sort_field, $sorted)); + + return $results; } @@ -1443,19 +1585,26 @@ */ public function search_once($folder = null, $str = 'ALL') { - if (!$str) { - $str = 'ALL'; - } - - if (!strlen($folder)) { - $folder = $this->folder; - } - if (!$this->check_connection()) { return new rcube_result_index(); } - $index = $this->conn->search($folder, $str, true); + if (!$str) { + $str = 'ALL'; + } + + // multi-folder search + if (is_array($folder) && count($folder) > 1) { + $searcher = new rcube_imap_search($this->options, $this->conn); + $index = $searcher->exec($folder, $str, $this->default_charset); + } + else { + $folder = is_array($folder) ? $folder[0] : $folder; + if (!strlen($folder)) { + $folder = $this->folder; + } + $index = $this->conn->search($folder, $str, true); + } return $index; } @@ -1500,7 +1649,7 @@ // but I've seen that Courier doesn't support UTF-8) if ($threads->is_error() && $charset && $charset != 'US-ASCII') { $threads = $this->conn->thread($folder, $this->threading, - $this->convert_criteria($criteria, $charset), true, 'US-ASCII'); + self::convert_criteria($criteria, $charset), true, 'US-ASCII'); } return $threads; @@ -1514,7 +1663,7 @@ // but I've seen Courier with disabled UTF-8 support) if ($messages->is_error() && $charset && $charset != 'US-ASCII') { $messages = $this->conn->sort($folder, $sort_field, - $this->convert_criteria($criteria, $charset), true, 'US-ASCII'); + self::convert_criteria($criteria, $charset), true, 'US-ASCII'); } if (!$messages->is_error()) { @@ -1529,7 +1678,7 @@ // Error, try with US-ASCII (some servers may support only US-ASCII) if ($messages->is_error() && $charset && $charset != 'US-ASCII') { $messages = $this->conn->search($folder, - $this->convert_criteria($criteria, $charset), true); + self::convert_criteria($criteria, $charset), true); } $this->search_sorted = false; @@ -1547,7 +1696,7 @@ * * @return string Search string */ - protected function convert_criteria($str, $charset, $dest_charset='US-ASCII') + public static function convert_criteria($str, $charset, $dest_charset='US-ASCII') { // convert strings to US_ASCII if (preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) { @@ -1556,12 +1705,15 @@ $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); - if ($string === false) { + + if ($string === false || !strlen($string)) { continue; } + $res .= substr($str, $last, $m[1] - $last - 1) . rcube_imap_generic::escape($string); $last = $m[0] + $string_offset - 1; } + if ($last < strlen($str)) { $res .= substr($str, $last, strlen($str)-$last); } @@ -1583,10 +1735,28 @@ public function refresh_search() { if (!empty($this->search_string)) { - $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field); + $this->search( + is_object($this->search_set) ? $this->search_set->get_parameters('MAILBOX') : '', + $this->search_string, + $this->search_charset, + $this->search_sort_field + ); } return $this->get_search_set(); + } + + /** + * Flag certain result subsets as 'incomplete'. + * For subsequent refresh_search() calls to only refresh the updated parts. + */ + protected function set_search_dirty($folder) + { + if ($this->search_set && is_a($this->search_set, 'rcube_result_multifolder')) { + if ($subset = $this->search_set->get_set($folder)) { + $subset->incomplete = $this->search_set->incomplete = true; + } + } } @@ -1601,6 +1771,11 @@ */ public function get_message_headers($uid, $folder = null, $force = false) { + // decode combined UID-folder identifier + if (preg_match('/^\d+-.+/', $uid)) { + list($uid, $folder) = explode('-', $uid, 2); + } + if (!strlen($folder)) { $folder = $this->folder; } @@ -1615,6 +1790,9 @@ else { $headers = $this->conn->fetchHeader( $folder, $uid, true, true, $this->get_fetch_headers()); + + if (is_object($headers)) + $headers->folder = $folder; } return $headers; @@ -1634,6 +1812,11 @@ { if (!strlen($folder)) { $folder = $this->folder; + } + + // decode combined UID-folder identifier + if (preg_match('/^\d+-.+/', $uid)) { + list($uid, $folder) = explode('-', $uid, 2); } // Check internal cache @@ -1679,7 +1862,7 @@ $this->struct_charset = $this->structure_charset($structure); } - $headers->ctype = strtolower($headers->ctype); + $headers->ctype = @strtolower($headers->ctype); // Here we can recognize malformed BODYSTRUCTURE and // 1. [@TODO] parse the message in other way to create our own message structure @@ -1774,6 +1957,16 @@ for ($i=1; $i<count($part); $i++) { if (!is_array($part[$i])) { $struct->ctype_secondary = strtolower($part[$i]); + + // read content type parameters + if (is_array($part[$i+1])) { + $struct->ctype_parameters = array(); + for ($j=0; $j<count($part[$i+1]); $j+=2) { + $param = strtolower($part[$i+1][$j]); + $struct->ctype_parameters[$param] = $part[$i+1][$j+1]; + } + } + break; } } @@ -2181,36 +2374,38 @@ /** * Returns the whole message source as string (or saves to a file) * - * @param int $uid Message UID - * @param resource $fp File pointer to save the message + * @param int $uid Message UID + * @param resource $fp File pointer to save the message + * @param string $part Optional message part ID * * @return string Message source string */ - public function get_raw_body($uid, $fp=null) + public function get_raw_body($uid, $fp=null, $part = null) { if (!$this->check_connection()) { return null; } return $this->conn->handlePartBody($this->folder, $uid, - true, null, null, false, $fp); + true, $part, null, false, $fp); } /** * Returns the message headers as string * - * @param int $uid Message UID + * @param int $uid Message UID + * @param string $part Optional message part ID * * @return string Message headers string */ - public function get_raw_headers($uid) + public function get_raw_headers($uid, $part = null) { if (!$this->check_connection()) { return null; } - return $this->conn->fetchPartHeader($this->folder, $uid, true); + return $this->conn->fetchPartHeader($this->folder, $uid, true, $part); } @@ -2282,6 +2477,8 @@ $this->clear_message_cache($folder, $all_mode ? null : explode(',', $uids)); } } + + $this->set_search_dirty($folder); } return $result; @@ -2329,6 +2526,17 @@ if ($saved) { // increase messagecount of the target folder $this->set_messagecount($folder, 'ALL', 1); + + $this->plugins->exec_hook('message_saved', array( + 'folder' => $folder, + 'message' => $message, + 'headers' => $headers, + 'is_file' => $is_file, + 'flags' => $flags, + 'date' => $date, + 'binary' => $binary, + 'result' => $saved, + )); } return $saved; @@ -2380,6 +2588,9 @@ if ($moved) { $this->clear_messagecount($from_mbox); $this->clear_messagecount($to_mbox); + + $this->set_search_dirty($from_mbox); + $this->set_search_dirty($to_mbox); } // moving failed else if ($to_trash && $config->get('delete_always', false)) { @@ -2396,8 +2607,8 @@ if ($this->search_threads || $all_mode) { $this->refresh_search(); } - else { - $this->search_set->filter(explode(',', $uids)); + else if (!$this->search_set->incomplete) { + $this->search_set->filter(explode(',', $uids), $this->folder); } } @@ -2484,13 +2695,15 @@ // unset threads internal cache unset($this->icache['threads']); + $this->set_search_dirty($folder); + // remove message ids from search set if ($this->search_set && $folder == $this->folder) { // threads are too complicated to just remove messages from set if ($this->search_threads || $all_mode) { $this->refresh_search(); } - else { + else if (!$this->search_set->incomplete) { $this->search_set->filter(explode(',', $uids)); } } @@ -2590,7 +2803,7 @@ } // Give plugins a chance to provide a list of folders - $data = rcube::get_instance()->plugins->exec_hook('storage_folders', + $data = $this->plugins->exec_hook('storage_folders', array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LSUB')); if (isset($data['folders'])) { @@ -2722,7 +2935,7 @@ } // Give plugins a chance to provide a list of folders - $data = rcube::get_instance()->plugins->exec_hook('storage_folders', + $data = $this->plugins->exec_hook('storage_folders', array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST')); if (isset($data['folders'])) { @@ -2803,7 +3016,7 @@ * @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) + protected function list_folders_update(&$result, $type = null) { $namespace = $this->get_namespace(); $search = array(); @@ -2880,14 +3093,15 @@ /** * Get mailbox quota information - * added by Nuny + * + * @param string $folder Folder name * * @return mixed Quota info or False if not supported */ - public function get_quota() + public function get_quota($folder = null) { if ($this->get_capability('QUOTA') && $this->check_connection()) { - return $this->conn->getQuota(); + return $this->conn->getQuota($folder); } return false; @@ -2962,6 +3176,16 @@ } $result = $this->conn->createFolder($folder, $type ? array("\\" . ucfirst($type)) : null); + + // it's quite often situation that we're trying to create and subscribe + // a folder that already exist, but is unsubscribed + if (!$result) { + if ($this->get_response_code() == rcube_storage::ALREADYEXISTS + || preg_match('/already exists/i', $this->get_error_str()) + ) { + $result = true; + } + } // try to subscribe it if ($result) { @@ -3110,12 +3334,14 @@ // request \Subscribed flag in LIST response as performance improvement for folder_exists() $folders = $this->conn->listMailboxes('', '*', array('SUBSCRIBED'), array('SPECIAL-USE')); - foreach ($folders as $folder) { - if ($flags = $this->conn->data['LIST'][$folder]) { - foreach ($types as $type) { - if (in_array($type, $flags)) { - $type = strtolower(substr($type, 1)); - $special[$type] = $folder; + if (!empty($folders)) { + foreach ($folders as $folder) { + if ($flags = $this->conn->data['LIST'][$folder]) { + foreach ($types as $type) { + if (in_array($type, $flags)) { + $type = strtolower(substr($type, 1)); + $special[$type] = $folder; + } } } } @@ -3737,8 +3963,16 @@ // @TODO: Honor MAXSIZE and DEPTH options foreach ($queries as $attrib => $entry) { - if ($result = $this->conn->getAnnotation($folder, $entry, $attrib)) { - $res = array_merge_recursive($res, $result); + $result = $this->conn->getAnnotation($folder, $entry, $attrib); + + // an error, invalidate any previous getAnnotation() results + if (!is_array($result)) { + return null; + } + else { + foreach ($result as $fldr => $data) { + $res[$fldr] = array_merge((array) $res[$fldr], $data); + } } } } @@ -3955,59 +4189,80 @@ */ public function sort_folder_list($a_folders, $skip_default = false) { - $a_out = $a_defaults = $folders = array(); - - $delimiter = $this->get_hierarchy_delimiter(); $specials = array_merge(array('INBOX'), array_values($this->get_special_folders())); + $folders = array(); - // find default folders and skip folders starting with '.' + // convert names to UTF-8 and skip folders starting with '.' foreach ($a_folders as $folder) { - if ($folder[0] == '.') { + if ($folder[0] != '.') { + // for better performance skip encoding conversion + // if the string does not look like UTF7-IMAP + $folders[$folder] = strpos($folder, '&') === false ? $folder : rcube_charset::convert($folder, 'UTF7-IMAP'); + } + } + + // sort folders + // asort($folders, SORT_LOCALE_STRING) is not properly sorting case sensitive names + uasort($folders, array($this, 'sort_folder_comparator')); + + $folders = array_keys($folders); + + if ($skip_default) { + return $folders; + } + + // force the type of folder name variable (#1485527) + $folders = array_map('strval', $folders); + $out = array(); + + // finally we must put special folders on top and rebuild the list + // to move their subfolders where they belong... + $specials = array_unique(array_intersect($specials, $folders)); + $folders = array_merge($specials, array_diff($folders, $specials)); + + $this->sort_folder_specials(null, $folders, $specials, $out); + + return $out; + } + + /** + * Recursive function to put subfolders of special folders in place + */ + protected function sort_folder_specials($folder, &$list, &$specials, &$out) + { + while (list($key, $name) = each($list)) { + if ($folder === null || strpos($name, $folder.$this->delimiter) === 0) { + $out[] = $name; + unset($list[$key]); + + if (!empty($specials) && ($found = array_search($name, $specials)) !== false) { + unset($specials[$found]); + $this->sort_folder_specials($name, $list, $specials, $out); + } + } + } + + reset($list); + } + + /** + * Callback for uasort() that implements correct + * locale-aware case-sensitive sorting + */ + protected function sort_folder_comparator($str1, $str2) + { + $path1 = explode($this->delimiter, $str1); + $path2 = explode($this->delimiter, $str2); + + foreach ($path1 as $idx => $folder1) { + $folder2 = $path2[$idx]; + + if ($folder1 === $folder2) { continue; } - if (!$skip_default && ($p = array_search($folder, $specials)) !== false && !$a_defaults[$p]) { - $a_defaults[$p] = $folder; - } - else { - $folders[$folder] = rcube_charset::convert($folder, 'UTF7-IMAP'); - } + return strcoll($folder1, $folder2); } - - // sort folders and place defaults on the top - asort($folders, SORT_LOCALE_STRING); - ksort($a_defaults); - $folders = array_merge($a_defaults, array_keys($folders)); - - // finally we must rebuild the list to move - // subfolders of default folders to their place... - // ...also do this for the rest of folders because - // asort() is not properly sorting case sensitive names - while (list($key, $folder) = each($folders)) { - // set the type of folder name variable (#1485527) - $a_out[] = (string) $folder; - unset($folders[$key]); - $this->rsort($folder, $delimiter, $folders, $a_out); - } - - return $a_out; - } - - - /** - * Recursive method for sorting folders - */ - 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); - } - } - reset($list); } -- Gitblit v1.9.1