From e37e7dda37f593e44d61bec0fe611b4de99c5791 Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Fri, 18 Nov 2011 09:33:44 -0500 Subject: [PATCH] - Fix typo in _list_unsubscribed() (#1488186) --- program/include/rcube_imap.php | 337 ++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 233 insertions(+), 104 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index f37cfda..a0a5f81 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -32,7 +32,6 @@ */ class rcube_imap { - public $debug_level = 1; public $skip_deleted = false; public $page_size = 10; public $list_page = 1; @@ -318,6 +317,19 @@ /** + * Activate/deactivate debug mode + * + * @param boolean $dbg True if IMAP conversation should be logged + * @access public + */ + function set_debug($dbg = true) + { + $this->options['debug'] = $dbg; + $this->conn->setDebug($dbg, array($this, 'debug_handler')); + } + + + /** * Set default message charset * * This will be used for message decoding if a charset specification is not available @@ -466,7 +478,7 @@ */ function get_mailbox_name() { - return $this->conn->connected() ? $this->mailbox : ''; + return $this->mailbox; } @@ -1361,6 +1373,11 @@ $this->_messagecount($mailbox, 'ALL', true); $result = 0; + + if (empty($old)) { + return $result; + } + $new = $this->get_folder_stats($mailbox); // got new messages @@ -1508,7 +1525,7 @@ // I didn't found that SEARCH should return sorted IDs if (is_array($a_index)) sort($a_index); - } else if ($max = $this->_messagecount($mailbox)) { + } else if ($max = $this->_messagecount($mailbox, 'ALL', true, false)) { $a_index = range(1, $max); } @@ -1694,7 +1711,7 @@ } if ($orig_criteria == 'ALL') { - $max = $this->_messagecount($mailbox); + $max = $this->_messagecount($mailbox, 'ALL', true, false); $a_messages = $max ? range(1, $max) : array(); } else { @@ -1961,6 +1978,10 @@ } $headers = $this->get_headers($uid, $mailbox); + + // message doesn't exist? + if (empty($headers)) + return null; // structure might be cached if (!empty($headers->structure)) @@ -2320,9 +2341,14 @@ // decode filename if (!empty($filename_mime)) { - $part->filename = rcube_imap::decode_mime_string($filename_mime, - $part->charset ? $part->charset : ($this->struct_charset ? $this->struct_charset : - rc_detect_encoding($filename_mime, $this->default_charset))); + if (!empty($part->charset)) + $charset = $part->charset; + else if (!empty($this->struct_charset)) + $charset = $this->struct_charset; + else + $charset = rc_detect_encoding($filename_mime, $this->default_charset); + + $part->filename = rcube_imap::decode_mime_string($filename_mime, $charset); } else if (!empty($filename_encoded)) { // decode filename according to RFC 2231, Section 4 @@ -2330,6 +2356,7 @@ $filename_charset = $fmatches[1]; $filename_encoded = $fmatches[2]; } + $part->filename = rcube_charset_convert(urldecode($filename_encoded), $filename_charset); } } @@ -2366,24 +2393,22 @@ */ function &get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false) { - // get part encoding if not provided + // get part data if not provided if (!is_object($o_part)) { $structure = $this->conn->getStructure($this->mailbox, $uid, true); + $part_data = rcube_imap_generic::getStructurePartData($structure, $part); $o_part = new rcube_message_part; - $o_part->ctype_primary = strtolower(rcube_imap_generic::getStructurePartType($structure, $part)); - $o_part->encoding = strtolower(rcube_imap_generic::getStructurePartEncoding($structure, $part)); - $o_part->charset = rcube_imap_generic::getStructurePartCharset($structure, $part); + $o_part->ctype_primary = $part_data['type']; + $o_part->encoding = $part_data['encoding']; + $o_part->charset = $part_data['charset']; + $o_part->size = $part_data['size']; } - // TODO: Add caching for message parts - - if (!$part) { - $part = 'TEXT'; + if ($o_part && $o_part->size) { + $body = $this->conn->handlePartBody($this->mailbox, $uid, true, + $part ? $part : 'TEXT', $o_part->encoding, $print, $fp); } - - $body = $this->conn->handlePartBody($this->mailbox, $uid, true, $part, - $o_part->encoding, $print, $fp); if ($fp || $print) { return true; @@ -2396,7 +2421,11 @@ if (!$skip_charset_conv) { if (!$o_part->charset || strtoupper($o_part->charset) == 'US-ASCII') { - $o_part->charset = $this->default_charset; + // try to extract charset information from HTML meta tag (#1488125) + if ($o_part->ctype_secondary == 'html' && preg_match('/<meta[^>]+charset=([a-z0-9-_]+)/i', $body, $m)) + $o_part->charset = strtoupper($m[1]); + else + $o_part->charset = $this->default_charset; } $body = rcube_charset_convert($body, $o_part->charset); } @@ -2531,7 +2560,7 @@ * @param string $headers Headers string if $message contains only the body * @param boolean $is_file True if $message is a filename * - * @return boolean True on success, False on error + * @return int|bool Appended message UID or True on success, False on error */ function save_message($mailbox, &$message, $headers='', $is_file=false) { @@ -2582,10 +2611,14 @@ // make sure mailbox exists if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) { - if (in_array($to_mbox, $this->default_folders)) - $this->create_mailbox($to_mbox, true); - else + if (in_array($to_mbox, $this->default_folders)) { + if (!$this->create_mailbox($to_mbox, true)) { + return false; + } + } + else { return false; + } } $config = rcmail::get_instance()->config; @@ -2663,10 +2696,14 @@ // make sure mailbox exists if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) { - if (in_array($to_mbox, $this->default_folders)) - $this->create_mailbox($to_mbox, true); - else + if (in_array($to_mbox, $this->default_folders)) { + if (!$this->create_mailbox($to_mbox, true)) { + return false; + } + } + else { return false; + } } // copy messages @@ -2914,50 +2951,22 @@ /** * Public method for listing subscribed folders * - * @param string $root Optional root folder - * @param string $name Optional name pattern - * @param string $filter Optional filter - * @param string $rights Optional ACL requirements + * @param string $root Optional root folder + * @param string $name Optional name pattern + * @param string $filter Optional filter + * @param string $rights Optional ACL requirements + * @param bool $skip_sort Enable to return unsorted list (for better performance) * - * @return array List of mailboxes/folders + * @return array List of folders * @access public */ - function list_mailboxes($root='', $name='*', $filter=null, $rights=null) - { - $a_mboxes = $this->_list_mailboxes($root, $name, $filter, $rights); - - // INBOX should always be available - if ((!$filter || $filter == 'mail') && !in_array('INBOX', $a_mboxes)) { - array_unshift($a_mboxes, 'INBOX'); - } - - // sort mailboxes - $a_mboxes = $this->_sort_mailbox_list($a_mboxes); - - return $a_mboxes; - } - - - /** - * Private method for mailbox listing - * - * @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 mailboxes/folders - * @see rcube_imap::list_mailboxes() - * @access private - */ - private function _list_mailboxes($root='', $name='*', $filter=null, $rights=null) + function list_mailboxes($root='', $name='*', $filter=null, $rights=null, $skip_sort=false) { $cache_key = $root.':'.$name; if (!empty($filter)) { $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter)); } $cache_key .= ':'.$rights; - $cache_key = 'mailboxes.'.md5($cache_key); // get cached folder list @@ -2966,6 +2975,48 @@ return $a_mboxes; } + $a_mboxes = $this->_list_mailboxes($root, $name, $filter, $rights); + + if (!is_array($a_mboxes)) { + return array(); + } + + // filter folders list according to rights requirements + if ($rights && $this->get_capability('ACL')) { + $a_mboxes = $this->filter_rights($a_mboxes, $rights); + } + + // INBOX should always be available + if ((!$filter || $filter == 'mail') && !in_array('INBOX', $a_mboxes)) { + array_unshift($a_mboxes, 'INBOX'); + } + + // sort mailboxes (always sort for cache) + if (!$skip_sort || $this->cache) { + $a_mboxes = $this->_sort_mailbox_list($a_mboxes); + } + + // write mailboxlist to cache + $this->update_cache($cache_key, $a_mboxes); + + return $a_mboxes; + } + + + /** + * Private method for mailbox 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_mailboxes() + * @access private + */ + private function _list_mailboxes($root='', $name='*', $filter=null, $rights=null) + { $a_defaults = $a_out = array(); // Give plugins a chance to provide a list of mailboxes @@ -2976,7 +3027,7 @@ $a_folders = $data['folders']; } else if (!$this->conn->connected()) { - return array(); + return null; } else { // Server supports LIST-EXTENDED, we can use selection options @@ -3024,14 +3075,6 @@ $a_folders = array(); } - // filter folders list according to rights requirements - if ($rights && $this->get_capability('ACL')) { - $a_folders = $this->filter_rights($a_folders, $rights); - } - - // write mailboxlist to cache - $this->update_cache($cache_key, $a_folders); - return $a_folders; } @@ -3039,16 +3082,29 @@ /** * Get a list of all folders available on the IMAP server * - * @param string $root IMAP root dir - * @param string $name Optional name pattern - * @param mixed $filter Optional filter - * @param string $rights Optional ACL requirements + * @param string $root IMAP root dir + * @param string $name Optional name pattern + * @param mixed $filter Optional filter + * @param string $rights Optional ACL requirements + * @param bool $skip_sort Enable to return unsorted list (for better performance) * * @return array Indexed array with folder names */ - function list_unsubscribed($root='', $name='*', $filter=null, $rights=null) + function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false) { - // @TODO: caching + $cache_key = $root.':'.$name; + if (!empty($filter)) { + $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter)); + } + $cache_key .= ':'.$rights; + $cache_key = 'mailboxes.list.'.md5($cache_key); + + // get cached folder list + $a_mboxes = $this->get_cache($cache_key); + if (is_array($a_mboxes)) { + return $a_mboxes; + } + // Give plugins a chance to provide a list of mailboxes $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list', array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST')); @@ -3058,7 +3114,7 @@ } else { // retrieve list of folders from IMAP server - $a_mboxes = $this->conn->listMailboxes($root, $name); + $a_mboxes = $this->_list_unsubscribed($root, $name); } if (!is_array($a_mboxes)) { @@ -3070,15 +3126,89 @@ array_unshift($a_mboxes, 'INBOX'); } + // cache folder attributes + if ($root == '' && $name == '*' && empty($filter)) { + $this->update_cache('mailboxes.attributes', $this->conn->data['LIST']); + } + // filter folders list according to rights requirements if ($rights && $this->get_capability('ACL')) { $a_folders = $this->filter_rights($a_folders, $rights); } // filter folders and sort them - $a_mboxes = $this->_sort_mailbox_list($a_mboxes); + if (!$skip_sort) { + $a_mboxes = $this->_sort_mailbox_list($a_mboxes); + } + + // write mailboxlist to cache + $this->update_cache($cache_key, $a_mboxes); return $a_mboxes; + } + + + /** + * Private method for mailbox listing (LIST) + * + * @param string $root Optional root folder + * @param string $name Optional name pattern + * + * @return array List of folders + * @see rcube_imap::list_unsubscribed() + */ + private function _list_unsubscribed($root='', $name='*') + { + $result = $this->conn->listMailboxes($root, $name); + + if (!is_array($result)) { + 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(); + + // 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]; + } + } + } + } + + 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) { + $list = $this->conn->listMailboxes($prefix, $name); + + if (!empty($list)) { + $result = array_merge($result, $list); + } + } + } + } + + return $result; } @@ -3354,8 +3484,8 @@ foreach ($this->namespace as $type => $namespace) { if (is_array($namespace)) { foreach ($namespace as $ns) { - if (strlen($ns[0])) { - if ((strlen($ns[0])>1 && $mailbox == substr($ns[0], 0, -1)) + if ($len = strlen($ns[0])) { + if (($len > 1 && $mailbox == substr($ns[0], 0, -1)) || strpos($mailbox, $ns[0]) === 0 ) { return $type; @@ -3409,30 +3539,29 @@ /** - * Gets folder options from LIST response, e.g. \Noselect, \Noinferiors + * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors * * @param string $mailbox Folder name - * @param bool $force Set to True if options should be refreshed - * Options are available after LIST command only + * @param bool $force Set to True if attributes should be refreshed * * @return array Options list */ - function mailbox_options($mailbox, $force=false) + function mailbox_attributes($mailbox, $force=false) { - if ($mailbox == 'INBOX') { - return array(); + // get attributes directly from LIST command + if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$mailbox])) { + $opts = $this->conn->data['LIST'][$mailbox]; + } + // get cached folder attributes + else if (!$force) { + $opts = $this->get_cache('mailboxes.attributes'); + $opts = $opts[$mailbox]; } - if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mailbox])) { - if ($force) { - $this->conn->listMailboxes('', $mailbox); - } - else { - return array(); - } + if (!is_array($opts)) { + $this->conn->listMailboxes('', $mailbox); + $opts = $this->conn->data['LIST'][$mailbox]; } - - $opts = $this->conn->data['LIST'][$mailbox]; return is_array($opts) ? $opts : array(); } @@ -3515,17 +3644,17 @@ } } - $options['name'] = $mailbox; - $options['options'] = $this->mailbox_options($mailbox, true); - $options['namespace'] = $this->mailbox_namespace($mailbox); - $options['rights'] = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array(); - $options['special'] = in_array($mailbox, $this->default_folders); + $options['name'] = $mailbox; + $options['attributes'] = $this->mailbox_attributes($mailbox, true); + $options['namespace'] = $this->mailbox_namespace($mailbox); + $options['rights'] = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array(); + $options['special'] = in_array($mailbox, $this->default_folders); // Set 'noselect' and 'norename' flags - if (is_array($options['options'])) { - foreach ($options['options'] as $opt) { - $opt = strtolower($opt); - if ($opt == '\noselect' || $opt == '\nonexistent') { + if (is_array($options['attributes'])) { + foreach ($options['attributes'] as $attrib) { + $attrib = strtolower($attrib); + if ($attrib == '\noselect' || $attrib == '\nonexistent') { $options['noselect'] = true; } } -- Gitblit v1.9.1