From a3644638aaf0418598196a870204e0b632a4c8ad Mon Sep 17 00:00:00 2001 From: Thomas Bruederli <thomas@roundcube.net> Date: Fri, 17 Apr 2015 06:28:40 -0400 Subject: [PATCH] Allow preference sections to define CSS class names --- program/lib/Roundcube/rcube_imap.php | 231 +++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 151 insertions(+), 80 deletions(-) diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 4b32c46..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 @@ -761,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); } @@ -1370,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 @@ -1506,7 +1520,7 @@ $folder = $this->folder; } - $plugin = rcube::get_instance()->plugins->exec_hook('imap_search_before', array( + $plugin = $this->plugins->exec_hook('imap_search_before', array( 'folder' => $folder, 'search' => $search, 'charset' => $charset, @@ -1691,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); } @@ -1845,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 @@ -1940,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; } } @@ -2347,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); } @@ -2498,7 +2527,7 @@ // increase messagecount of the target folder $this->set_messagecount($folder, 'ALL', 1); - rcube::get_instance()->plugins->exec_hook('message_saved', array( + $this->plugins->exec_hook('message_saved', array( 'folder' => $folder, 'message' => $message, 'headers' => $headers, @@ -2774,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'])) { @@ -2906,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'])) { @@ -2987,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(); @@ -3064,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; @@ -3146,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) { @@ -3294,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; + } } } } @@ -3921,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); + } } } } @@ -4139,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