vbenincasa
2010-06-09 d0b981757ab416dfd182e6b91e7f9a66132116f9
program/include/rcube_imap.php
@@ -26,7 +26,8 @@
 *
 * @package    Mail
 * @author     Thomas Bruederli <roundcube@gmail.com>
 * @version    1.6
 * @author     Aleksander Machniak <alec@alec.pl>
 * @version    2.0
 */
class rcube_imap
{
@@ -52,7 +53,7 @@
    private $default_folders = array('INBOX');
    private $icache = array();
    private $cache = array();
    private $cache_keys = array();
    private $cache_keys = array();
    private $cache_changes = array();
    private $uid_id_map = array();
    private $msg_headers = array();
@@ -91,7 +92,7 @@
     */
    function connect($host, $user, $pass, $port=143, $use_ssl=null)
    {
        // check for Open-SSL support in PHP build
        // check for OpenSSL support in PHP build
        if ($use_ssl && extension_loaded('openssl'))
            $this->options['ssl_mode'] = $use_ssl == 'imaps' ? 'ssl' : $use_ssl;
        else if ($use_ssl) {
@@ -107,7 +108,7 @@
        do {
            $data = rcmail::get_instance()->plugins->exec_hook('imap_connect',
                array('host' => $host, 'user' => $user, 'attempt' => ++$attempt));
            if (!empty($data['pass']))
                $pass = $data['pass'];
@@ -153,7 +154,7 @@
     * @access public
     */
    function close()
    {
    {
        if ($this->conn && $this->conn->connected())
            $this->conn->close();
        $this->write_cache();
@@ -170,7 +171,7 @@
    {
        $this->close();
        $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
        // issue SELECT command to restore connection status
        if ($this->mailbox)
            $this->conn->select($this->mailbox);
@@ -200,7 +201,7 @@
        $this->root_dir = $root;
        $this->options['rootdir'] = $root;
        if (empty($this->delimiter))
            $this->get_hierarchy_delimiter();
    }
@@ -282,7 +283,7 @@
    {
        $this->page_size = (int)$size;
    }
    /**
     * Save a set of message ids for future message listing methods
@@ -296,7 +297,9 @@
    {
        if (is_array($str) && $msgs == null)
            list($str, $msgs, $charset, $sort_field, $threads) = $str;
        if ($msgs != null && !is_array($msgs))
        if ($msgs === false)
            $msgs = array();
        else if ($msgs != null && !is_array($msgs))
            $msgs = explode(',', $msgs);
        $this->search_string     = $str;
@@ -357,7 +360,7 @@
    function set_threading($enable=false)
    {
        $this->threading = false;
        if ($enable) {
            if ($this->get_capability('THREAD=REFS'))
                $this->threading = 'REFS';
@@ -408,16 +411,18 @@
    /**
     * Get message count for a specific mailbox
     *
     * @param   string   Mailbox/folder name
     * @param   string   Mode for count [ALL|THREADS|UNSEEN|RECENT]
     * @param   boolean  Force reading from server and update cache
     * @return  int      Number of messages
     * @access  public
     * @param  string  Mailbox/folder name
     * @param  string  Mode for count [ALL|THREADS|UNSEEN|RECENT]
     * @param  boolean Force reading from server and update cache
     * @param  boolean Enables storing folder status info (max UID/count),
     *                 required for mailbox_status()
     * @return int     Number of messages
     * @access public
     */
    function messagecount($mbox_name='', $mode='ALL', $force=false)
    function messagecount($mbox_name='', $mode='ALL', $force=false, $status=true)
    {
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        return $this->_messagecount($mailbox, $mode, $force);
        return $this->_messagecount($mailbox, $mode, $force, $status);
    }
@@ -427,7 +432,7 @@
     * @access  private
     * @see     rcube_imap::messagecount()
     */
    private function _messagecount($mailbox='', $mode='ALL', $force=false)
    private function _messagecount($mailbox='', $mode='ALL', $force=false, $status=true)
    {
        $mode = strtoupper($mode);
@@ -441,9 +446,9 @@
            else
                return count((array)$this->search_set);
        }
        $a_mailbox_cache = $this->get_cache('messagecount');
        // return cached value
        if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
            return $a_mailbox_cache[$mailbox][$mode];
@@ -453,7 +458,10 @@
        if ($mode == 'THREADS') {
            $count = $this->_threadcount($mailbox, $msg_count);
            $_SESSION['maxuid'][$mailbox] = $msg_count ? $this->_id2uid($msg_count) : 0;
            if ($status) {
                $this->set_folder_stats($mailbox, 'cnt', $msg_count);
                $this->set_folder_stats($mailbox, 'maxuid', $msg_count ? $this->_id2uid($msg_count, $mailbox) : 0);
            }
        }
        // RECENT count is fetched a bit different
        else if ($mode == 'RECENT') {
@@ -466,26 +474,26 @@
            // get message count and store in cache
            if ($mode == 'UNSEEN')
                $search_str .= " UNSEEN";
            // get message count using SEARCH
            // not very performant but more precise (using UNDELETED)
            // disable THREADS for this request
            $threads = $this->threading;
            $this->threading = false;
            $index = $this->_search_index($mailbox, $search_str);
            $this->threading = $threads;
            $index = $this->conn->search($mailbox, $search_str);
            $count = is_array($index) ? count($index) : 0;
            if ($mode == 'ALL')
                $_SESSION['maxuid'][$mailbox] = $index ? $this->_id2uid(max($index)) : 0;
            if ($mode == 'ALL' && $status) {
                $this->set_folder_stats($mailbox, 'cnt', $count);
                $this->set_folder_stats($mailbox, 'maxuid', $index ? $this->_id2uid(max($index), $mailbox) : 0);
            }
        }
        else {
            if ($mode == 'UNSEEN')
                $count = $this->conn->countUnseen($mailbox);
            else {
                $count = $this->conn->countMessages($mailbox);
                $_SESSION['maxuid'][$mailbox] = $count ? $this->_id2uid($count) : 0;
                if ($status) {
                    $this->set_folder_stats($mailbox,'cnt', $count);
                    $this->set_folder_stats($mailbox, 'maxuid', $count ? $this->_id2uid($count, $mailbox) : 0);
                }
            }
        }
@@ -508,13 +516,13 @@
    {
        if (!empty($this->icache['threads']))
            return count($this->icache['threads']['tree']);
        list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox);
        $msg_count = count($msg_depth);
//    $this->update_thread_cache($mailbox, $thread_tree, $msg_depth, $has_children);
        return count($thread_tree);
        return count($thread_tree);
    }
@@ -528,7 +536,7 @@
     * @param   string   Sort order [ASC|DESC]
     * @param   boolean  Number of slice items to extract from result array
     * @return  array    Indexed array with message header objects
     * @access  public
     * @access  public
     */
    function list_headers($mbox_name='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
    {
@@ -597,7 +605,7 @@
            else
                $msg_index = array();
            if ($slice)
            if ($slice && $msg_index)
                $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice);
            // fetch reqested headers from server
@@ -643,14 +651,14 @@
        // return empty array if no messages found
        if (!is_array($a_msg_headers) || empty($a_msg_headers))
            return array();
        // use this class for message sorting
        $sorter = new rcube_header_sorter();
        $sorter->set_sequence_numbers($msg_index);
        $sorter->sort_headers($a_msg_headers);
        if ($this->sort_order == 'DESC')
            $a_msg_headers = array_reverse($a_msg_headers);
            $a_msg_headers = array_reverse($a_msg_headers);
        return array_values($a_msg_headers);
    }
@@ -696,7 +704,7 @@
            // get all threads
            list ($thread_tree, $msg_depth, $has_children) = $this->conn->thread(
                $mailbox, $this->threading, $this->skip_deleted ? 'UNDELETED' : '');
            // add to internal (fast) cache
            $this->icache['threads'] = array();
            $this->icache['threads']['tree'] = $thread_tree;
@@ -745,7 +753,7 @@
        // return empty array if no messages found
        if (!is_array($a_msg_headers) || empty($a_msg_headers))
            return array();
        // use this class for message sorting
        $sorter = new rcube_header_sorter();
        $sorter->set_sequence_numbers($all_ids);
@@ -812,9 +820,12 @@
            return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice);
        // search set is threaded, we need a new one
        if ($this->search_threads)
        if ($this->search_threads) {
            if (empty($this->search_set['tree']))
                return array();
            $this->search('', $this->search_string, $this->search_charset, $sort_field);
        }
        $msgs = $this->search_set;
        $a_msg_headers = array();
        $page = $page ? $page : $this->list_page;
@@ -898,7 +909,7 @@
            else {
                // for small result set we can fetch all messages headers
                $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
                // return empty array if no messages found
                if (!is_array($a_msg_headers) || empty($a_msg_headers))
                    return array();
@@ -935,8 +946,15 @@
    private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
    {
        // update search_set if previous data was fetched with disabled threading
        if (!$this->search_threads)
        if (!$this->search_threads) {
            if (empty($this->search_set))
                return array();
            $this->search('', $this->search_string, $this->search_charset, $sort_field);
        }
        // empty result
        if (empty($this->search_set['tree']))
            return array();
        $thread_tree = $this->search_set['tree'];
        $msg_depth = $this->search_set['depth'];
@@ -966,7 +984,7 @@
    private function _get_message_range($max, $page)
    {
        $start_msg = ($page-1) * $this->page_size;
        if ($page=='all') {
            $begin  = 0;
            $end    = $max;
@@ -983,10 +1001,10 @@
        if ($begin < 0) $begin = 0;
        if ($end < 0) $end = $max;
        if ($end > $max) $end = $max;
        return array($begin, $end);
    }
    /**
     * Fetches message headers
@@ -1028,33 +1046,71 @@
        return count($a_msg_headers);
    }
    /**
     * Fetches IDS of pseudo recent messages.
     * Returns current status of mailbox
     *
     * We compare the maximum UID to determine the number of
     * new messages because the RECENT flag is not reliable.
     *
     * @param string  Mailbox/folder name
     * @return array  List of recent message UIDs
     * @param string Mailbox/folder name
     * @return int   Folder status
     */
    function recent_uids($mbox_name = null, $nofetch = false)
    function mailbox_status($mbox_name = null)
    {
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        $old_maxuid = intval($_SESSION['maxuid'][$mailbox]);
        // refresh message count -> will update $_SESSION['maxuid'][$mailbox]
        $old = $this->get_folder_stats($mailbox);
        // refresh message count -> will update
        $this->_messagecount($mailbox, 'ALL', true);
        if ($_SESSION['maxuid'][$mailbox] > $old_maxuid) {
            $maxuid = max(1, $old_maxuid+1);
            return array_values((array)$this->conn->fetchHeaderIndex(
                $mailbox, "$maxuid:*", 'UID', $this->skip_deleted, true));
        }
        return array();
        $result = 0;
        $new = $this->get_folder_stats($mailbox);
        // got new messages
        if ($new['maxuid'] > $old['maxuid'])
            $result += 1;
        // some messages has been deleted
        if ($new['cnt'] < $old['cnt'])
            $result += 2;
        // @TODO: optional checking for messages flags changes (?)
        // @TODO: UIDVALIDITY checking
        return $result;
    }
    /**
     * Stores folder statistic data in session
     * @TODO: move to separate DB table (cache?)
     *
     * @param string Mailbox name
     * @param string Data name
     * @param mixed  Data value
     */
    private function set_folder_stats($mbox_name, $name, $data)
    {
        $_SESSION['folders'][$mbox_name][$name] = $data;
    }
    /**
     * Gets folder statistic data
     *
     * @param string Mailbox name
     * @return array Stats data
     */
    private function get_folder_stats($mbox_name)
    {
        if ($_SESSION['folders'][$mbox_name])
            return (array) $_SESSION['folders'][$mbox_name];
        else
            return array();
    }
    /**
     * Return sorted array of message IDs (not UIDs)
     *
@@ -1101,7 +1157,7 @@
            else {
                $a_index = $this->conn->fetchHeaderIndex($mailbox,
                   join(',', $this->search_set), $this->sort_field, $this->skip_deleted);
                if (is_array($a_index)) {
                    if ($this->sort_order=="ASC")
                        asort($a_index);
@@ -1139,20 +1195,20 @@
                $a_index = range(1, $max);
            }
            if ($this->sort_order == 'DESC')
            if ($a_index !== false && $this->sort_order == 'DESC')
                $a_index = array_reverse($a_index);
            $this->cache[$key] = $a_index;
        }
        // fetch complete message index
        else if ($this->get_capability('SORT')) {
            if ($a_index = $this->conn->sort($mailbox,
                $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')) {
                if ($this->sort_order == 'DESC')
                    $a_index = array_reverse($a_index);
                $this->cache[$key] = $a_index;
           }
            $a_index = $this->conn->sort($mailbox,
                $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '');
            if ($a_index !== false && $this->sort_order == 'DESC')
                $a_index = array_reverse($a_index);
            $this->cache[$key] = $a_index;
        }
        else if ($a_index = $this->conn->fetchHeaderIndex(
            $mailbox, "1:*", $this->sort_field, $this->skip_deleted)) {
@@ -1160,11 +1216,11 @@
                asort($a_index);
            else if ($this->sort_order=="DESC")
                arsort($a_index);
            $this->cache[$key] = array_keys($a_index);
        }
        return $this->cache[$key];
        return $this->cache[$key] !== false ? $this->cache[$key] : array();
    }
@@ -1209,7 +1265,7 @@
        list ($thread_tree) = $this->_fetch_threads($mailbox);
        $this->cache[$key] = $this->_flatten_threads($mailbox, $thread_tree);
        return $this->cache[$key];
    }
@@ -1234,7 +1290,7 @@
        if ($this->sort_order == 'DESC')
            $msg_index = array_reverse($msg_index);
        // flatten threads array
        $all_ids = array();
        foreach($msg_index as $root) {
@@ -1257,10 +1313,10 @@
        // fetch complete message index
        $a_message_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 'UID', $this->skip_deleted);
        if ($a_message_index === false)
            return false;
        foreach ($a_message_index as $id => $uid) {
            // message in cache at correct position
            if ($cache_index[$id] == $uid) {
@@ -1272,20 +1328,20 @@
            if (in_array((string)$uid, $cache_index, true)) {
                unset($cache_index[$id]);
            }
            // other message at this position
            if (isset($cache_index[$id])) {
                $for_remove[] = $cache_index[$id];
                unset($cache_index[$id]);
            }
            $for_update[] = $id;
        }
        // clear messages at wrong positions and those deleted that are still in cache_index
        // 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);
        if (!empty($cache_index))
            $this->remove_message_cache($cache_key, $cache_index);
@@ -1316,36 +1372,10 @@
    {
        if (!$str)
            return false;
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        $results = $this->_search_index($mailbox, $str, $charset, $sort_field);
        // 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')
        {
            // convert strings to US_ASCII
            if(preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) {
                $last = 0; $res = '';
                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, 'US-ASCII');
                    if (!$string)
                        continue;
                    $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string);
                    $last = $m[0] + $string_offset - 1;
                }
                if ($last < strlen($str))
                    $res .= substr($str, $last, strlen($str)-$last);
            }
            else // strings for conversion not found
                $res = $str;
            $results = $this->search($mbox_name, $res, NULL, $sort_field);
        }
        $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading);
@@ -1368,21 +1398,32 @@
            $criteria = 'UNDELETED '.$criteria;
        if ($this->threading) {
            list ($thread_tree, $msg_depth, $has_children) = $this->conn->thread(
                $mailbox, $this->threading, $criteria, $charset);
            $a_messages = $this->conn->thread($mailbox, $this->threading, $criteria, $charset);
            $a_messages = array(
                'tree'    => $thread_tree,
               'depth'   => $msg_depth,
               'children' => $has_children
            );
            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
            // but I've seen that Courier doesn't support UTF-8)
            if ($a_messages === false && $charset && $charset != 'US-ASCII')
                $a_messages = $this->conn->thread($mailbox, $this->threading,
                    $this->convert_criteria($criteria, $charset), 'US-ASCII');
            if ($a_messages !== false) {
                list ($thread_tree, $msg_depth, $has_children) = $a_messages;
                $a_messages = array(
                    'tree'    => $thread_tree,
                   'depth'   => $msg_depth,
                   'children' => $has_children
                );
            }
        }
        else if ($sort_field && $this->get_capability('SORT')) {
            $charset = $charset ? $charset : $this->default_charset;
            $a_messages = $this->conn->sort($mailbox, $sort_field, $criteria, false, $charset);
            if (!$a_messages)
               return array();
            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
            // but I've seen that Courier doesn't support UTF-8)
            if ($a_messages === false && $charset && $charset != 'US-ASCII')
                $a_messages = $this->conn->sort($mailbox, $sort_field,
                    $this->convert_criteria($criteria, $charset), false, 'US-ASCII');
        }
        else {
            if ($orig_criteria == 'ALL') {
@@ -1391,14 +1432,16 @@
            }
            else {
                $a_messages = $this->conn->search($mailbox,
                        ($charset ? "CHARSET $charset " : '') . $criteria);
                    ($charset ? "CHARSET $charset " : '') . $criteria);
           if (!$a_messages)
               return array();
                // Error, try with US-ASCII (some servers may support only US-ASCII)
                if ($a_messages === false && $charset && $charset != 'US-ASCII')
                    $a_messages = $this->conn->search($mailbox,
                        'CHARSET US-ASCII ' . $this->convert_criteria($criteria, $charset));
            // I didn't found that SEARCH always returns sorted IDs
            if (!$this->sort_field)
                sort($a_messages);
                // I didn't found that SEARCH should return sorted IDs
               if (is_array($a_messages) && !$this->sort_field)
                    sort($a_messages);
            }
        }
@@ -1406,10 +1449,10 @@
//      $a_mailbox_cache = get_cache('messagecount');
//      $a_mailbox_cache[$mailbox][$criteria] = sizeof($a_messages);
//      $this->update_cache('messagecount', $a_mailbox_cache);
        return $a_messages;
    }
    /**
     * Direct (real and simple) SEARCH request to IMAP server,
@@ -1425,13 +1468,46 @@
    {
        if (!$str)
            return false;
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        return $this->conn->search($mailbox, $str, $ret_uid);
    }
    /**
     * Converts charset of search criteria string
     *
     * @param  string  Search string
     * @param  string  Original charset
     * @param  string  Destination charset (default US-ASCII)
     * @return string  Search string
     * @access private
     */
    private 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)) {
            $last = 0; $res = '';
            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);
                if (!$string)
                    continue;
                $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string);
                $last = $m[0] + $string_offset - 1;
            }
            if ($last < strlen($str))
                $res .= substr($str, $last, strlen($str)-$last);
        }
        else // strings for conversion not found
            $res = $str;
        return $res;
    }
    /**
     * Sort thread
     *
@@ -1491,7 +1567,7 @@
    {
        if (empty($tree))
            return array();
        $index = array_combine(array_values($index), $index);
        // assign roots
@@ -1504,7 +1580,7 @@
            }
        }
        $index = array_values($index);
        $index = array_values($index);
        // create sorted array of roots
        $msg_index = array();
@@ -1534,13 +1610,12 @@
    {
        if (!empty($this->search_string))
            $this->search_set = $this->search('', $this->search_string, $this->search_charset,
               $this->search_sort_field, $this->search_threads);
       $this->search_sort_field, $this->search_threads);
        return $this->get_search_set();
    }
    /**
     * Check if the given message ID is part of the current search set
     *
@@ -1563,7 +1638,7 @@
     * Return message headers object of a specific message
     *
     * @param int     Message ID
     * @param string  Mailbox to read from
     * @param string  Mailbox to read from
     * @param boolean True if $id is the message UID
     * @param boolean True if we need also BODYSTRUCTURE in headers
     * @return object Message headers representation
@@ -1571,7 +1646,7 @@
    function get_headers($id, $mbox_name=NULL, $is_uid=true, $bodystr=false)
    {
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        $uid = $is_uid ? $id : $this->_id2uid($id);
        $uid = $is_uid ? $id : $this->_id2uid($id, $mailbox);
        // get cached headers
        if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
@@ -1585,7 +1660,7 @@
            if ($headers->uid && $headers->id)
                $this->uid_id_map[$mailbox][$headers->uid] = $headers->id;
            $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL, true);
            $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL);
        }
        return $headers;
@@ -1627,7 +1702,7 @@
        else
            $this->struct_charset = $this->_structure_charset($structure);
        // Here we can recognize malformed BODYSTRUCTURE and
        // Here we can recognize malformed BODYSTRUCTURE and
        // 1. [@TODO] parse the message in other way to create our own message structure
        // 2. or just show the raw message body.
        // Example of structure for malformed MIME message:
@@ -1655,7 +1730,7 @@
        return $struct;
    }
    /**
     * Build message part object
     *
@@ -1669,7 +1744,7 @@
        // multipart
        if (is_array($part[0])) {
            $struct->ctype_primary = 'multipart';
            // find first non-array entry
            for ($i=1; $i<count($part); $i++) {
                if (!is_array($part[$i])) {
@@ -1677,12 +1752,12 @@
                    break;
                }
            }
            $struct->mimetype = 'multipart/'.$struct->ctype_secondary;
            // build parts list for headers pre-fetching
            for ($i=0, $count=0; $i<count($part); $i++) {
                if (is_array($part[$i]) && count($part[$i]) > 3) {
                if (is_array($part[$i]) && count($part[$i]) > 4) {
                    // fetch message headers if message/rfc822
                    // or named part (could contain Content-Location header)
                    if (!is_array($part[$i][0])) {
@@ -1697,7 +1772,7 @@
                    }
                }
            }
            // pre-fetch headers of all parts (in one command for better performance)
            // @TODO: we could do this before _structure_part() call, to fetch
            // headers for parts on all levels
@@ -1712,7 +1787,7 @@
            }
            $struct->parts = array();
            for ($i=0, $count=0; $i<count($part); $i++) {
                if (is_array($part[$i]) && count($part[$i]) > 3) {
                if (is_array($part[$i]) && count($part[$i]) > 4) {
                    $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
                    $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
                    $mime_part_headers[$tmp_part_id], $raw_part_headers[$tmp_part_id]);
@@ -1732,17 +1807,17 @@
            $struct->ctype_parameters = array();
            for ($i=0; $i<count($part[2]); $i+=2)
                $struct->ctype_parameters[strtolower($part[2][$i])] = $part[2][$i+1];
            if (isset($struct->ctype_parameters['charset']))
                $struct->charset = $struct->ctype_parameters['charset'];
        }
        // read content encoding
        if (!empty($part[5]) && $part[5]!='NIL') {
            $struct->encoding = strtolower($part[5]);
            $struct->headers['content-transfer-encoding'] = $struct->encoding;
        }
        // get part size
        if (!empty($part[6]) && $part[6]!='NIL')
            $struct->size = intval($part[6]);
@@ -1757,7 +1832,7 @@
                for ($n=0; $n<count($part[$di][1]); $n+=2)
                    $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
        }
        // get child parts
        if (is_array($part[8]) && $di != 8) {
            $struct->parts = array();
@@ -1770,11 +1845,11 @@
        if (!empty($part[3]) && $part[3]!='NIL') {
            $struct->content_id = $part[3];
            $struct->headers['content-id'] = $part[3];
            if (empty($struct->disposition))
                $struct->disposition = 'inline';
        }
        // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
        if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
            if (empty($mime_headers)) {
@@ -1794,7 +1869,7 @@
                // get real content-type of message/rfc822
                if (preg_match('/^([a-z0-9_\/-]+)/i', $struct->real_headers['content-type'], $matches)) {
                    $struct->real_mimetype = strtolower($matches[1]);
                }
                }
            }
        }
@@ -1808,10 +1883,10 @@
        return $struct;
    }
    /**
     * Set attachment filename from message part structure
     * Set attachment filename from message part structure
     *
     * @access private
     * @param  object rcube_message_part Part object
@@ -1916,10 +1991,10 @@
        // decode filename
        if (!empty($filename_mime)) {
            $part->filename = rcube_imap::decode_mime_string($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)));
        }
        }
        else if (!empty($filename_encoded)) {
            // decode filename according to RFC 2231, Section 4
            if (preg_match("/^([^']*)'[^']*'(.*)$/", $filename_encoded, $fmatches)) {
@@ -1945,7 +2020,7 @@
                return $structure[2][1];
            $structure = $structure[0];
        }
    }
    }
    /**
@@ -1962,7 +2037,7 @@
    {
        // get part encoding if not provided
        if (!is_object($o_part)) {
            $structure_str = $this->conn->fetchStructureString($this->mailbox, $uid, true);
            $structure_str = $this->conn->fetchStructureString($this->mailbox, $uid, true);
            $structure = new rcube_mime_struct();
            // error or message not found
            if (!$structure->loadStructure($structure_str)) {
@@ -1974,7 +2049,7 @@
            $o_part->encoding      = strtolower($structure->getPartEncoding($part));
            $o_part->charset       = $structure->getPartCharset($part);
        }
        // TODO: Add caching for message parts
        if (!$part) $part = 'TEXT';
@@ -1986,14 +2061,14 @@
            return true;
        // convert charset (if text or message part)
        if ($o_part->ctype_primary=='text' || $o_part->ctype_primary=='message') {
        if ($body && ($o_part->ctype_primary == 'text' || $o_part->ctype_primary == 'message')) {
            // assume default if no charset specified
            if (empty($o_part->charset) || strtolower($o_part->charset) == 'us-ascii')
                $o_part->charset = $this->default_charset;
            $body = rcube_charset_convert($body, $o_part->charset);
        }
        return $body;
    }
@@ -2035,13 +2110,13 @@
    {
        return $this->conn->fetchPartHeader($this->mailbox, $uid, true);
    }
    /**
     * Sends the whole message source to stdout
     *
     * @param int  Message UID
     */
     */
    function print_raw_body($uid)
    {
        $this->conn->handlePartBody($this->mailbox, $uid, true, NULL, NULL, true);
@@ -2290,7 +2365,7 @@
            // unset threads internal cache
            unset($this->icache['threads']);
            // remove message ids from search set
            if ($this->search_set && $mailbox == $this->mailbox) {
                // threads are too complicated to just remove messages from set
@@ -2326,21 +2401,21 @@
    {
        $mailbox = !empty($mbox_name) ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        $msg_count = $this->_messagecount($mailbox, 'ALL');
        if (!$msg_count) {
            return 0;
        }
        $cleared = $this->conn->clearFolder($mailbox);
        // make sure the message count cache is cleared as well
        if ($cleared) {
            $this->clear_message_cache($mailbox.'.msg');
            $this->clear_message_cache($mailbox.'.msg');
            $a_mailbox_cache = $this->get_cache('messagecount');
            unset($a_mailbox_cache[$mailbox]);
            $this->update_cache('messagecount', $a_mailbox_cache);
        }
        return $cleared;
    }
@@ -2371,7 +2446,7 @@
     */
    private function _expunge($mailbox, $clear_cache=true, $uids=NULL)
    {
        if ($uids && $this->get_capability('UIDPLUS'))
        if ($uids && $this->get_capability('UIDPLUS'))
            $a_uids = is_array($uids) ? join(',', $uids) : $uids;
        else
            $a_uids = NULL;
@@ -2382,7 +2457,7 @@
            $this->clear_message_cache($mailbox.'.msg');
            $this->_clear_messagecount($mailbox);
        }
        return $result;
    }
@@ -2392,7 +2467,7 @@
     *
     * @param mixed  UIDs array or comma-separated list or '*' or '1:*'
     * @param string Mailbox name
     * @return array Two elements array with UIDs converted to list and ALL flag
     * @return array Two elements array with UIDs converted to list and ALL flag
     * @access private
     */
    private function _parse_uids($uids, $mailbox)
@@ -2409,7 +2484,7 @@
                    $uids = $this->conn->fetchUIDs($mailbox, array_keys($this->search_set['depth']));
                else
                    $uids = $this->conn->fetchUIDs($mailbox, $this->search_set);
                // save ID-to-UID mapping in local cache
                if (is_array($uids))
                    foreach ($uids as $id => $uid)
@@ -2437,7 +2512,7 @@
     * @param string Mailbox name
     * @return int   Message ID
     */
    function get_id($uid, $mbox_name=NULL)
    function get_id($uid, $mbox_name=NULL)
    {
        $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox;
        return $this->_uid2id($uid, $mailbox);
@@ -2464,7 +2539,7 @@
     * --------------------------------*/
    /**
     * Public method for mailbox listing.
     * Public method for listing subscribed folders
     *
     * Converts mailbox name with root dir first
     *
@@ -2478,10 +2553,10 @@
        $a_out = array();
        $a_mboxes = $this->_list_mailboxes($root, $filter);
        foreach ($a_mboxes as $mbox_row) {
            $name = $this->mod_mailbox($mbox_row, 'out');
            if (strlen($name))
        foreach ($a_mboxes as $idx => $mbox_row) {
            if ($name = $this->mod_mailbox($mbox_row, 'out'))
                $a_out[] = $name;
            unset($a_mboxes[$idx]);
        }
        // INBOX should always be available
@@ -2504,17 +2579,17 @@
     */
    private function _list_mailboxes($root='', $filter='*')
    {
        $a_defaults = $a_out = array();
        // get cached folder list
        // get cached folder list
        $a_mboxes = $this->get_cache('mailboxes');
        if (is_array($a_mboxes))
            return $a_mboxes;
        $a_defaults = $a_out = array();
        // Give plugins a chance to provide a list of mailboxes
        $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes',
            array('root'=>$root,'filter'=>$filter));
            array('root' => $root, 'filter' => $filter, 'mode' => 'LSUB'));
        if (isset($data['folders'])) {
            $a_folders = $data['folders'];
        }
@@ -2522,57 +2597,70 @@
            // retrieve list of folders from IMAP server
            $a_folders = $this->conn->listSubscribed($this->mod_mailbox($root), $filter);
        }
        if (!is_array($a_folders) || !sizeof($a_folders))
            $a_folders = array();
        // write mailboxlist to cache
        $this->update_cache('mailboxes', $a_folders);
        return $a_folders;
    }
    /**
     * Get a list of all folders available on the IMAP server
     *
     *
     * @param string IMAP root dir
     * @param string Optional filter for mailbox listing
     * @return array Indexed array with folder names
     */
    function list_unsubscribed($root='')
    function list_unsubscribed($root='', $filter='*')
    {
        static $sa_unsubscribed;
        if (is_array($sa_unsubscribed))
            return $sa_unsubscribed;
        // retrieve list of folders from IMAP server
        $a_mboxes = $this->conn->listMailboxes($this->mod_mailbox($root), '*');
        // Give plugins a chance to provide a list of mailboxes
        $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes',
            array('root' => $root, 'filter' => $filter, 'mode' => 'LIST'));
        // modify names with root dir
        foreach ($a_mboxes as $mbox_name) {
            $name = $this->mod_mailbox($mbox_name, 'out');
            if (strlen($name))
                $a_folders[] = $name;
        if (isset($data['folders'])) {
            $a_mboxes = $data['folders'];
        }
        else {
            // retrieve list of folders from IMAP server
            $a_mboxes = $this->conn->listMailboxes($this->mod_mailbox($root), $filter);
        }
        $a_folders = array();
        if (!is_array($a_mboxes))
            $a_mboxes = array();
        // modify names with root dir
        foreach ($a_mboxes as $idx => $mbox_name) {
            if ($name = $this->mod_mailbox($mbox_name, 'out'))
                $a_folders[] = $name;
            unset($a_mboxes[$idx]);
        }
        // INBOX should always be available
        if (!in_array('INBOX', $a_folders))
            array_unshift($a_folders, 'INBOX');
        // filter folders and sort them
        $sa_unsubscribed = $this->_sort_mailbox_list($a_folders);
        return $sa_unsubscribed;
        $a_folders = $this->_sort_mailbox_list($a_folders);
        return $a_folders;
    }
    /**
     * Get mailbox quota information
     * added by Nuny
     *
     *
     * @return mixed Quota info or False if not supported
     */
    function get_quota()
    {
        if ($this->get_capability('QUOTA'))
            return $this->conn->getQuota();
        return false;
    }
@@ -2582,7 +2670,7 @@
     *
     * @param array Mailbox name(s)
     * @return boolean True on success
     */
     */
    function subscribe($a_mboxes)
    {
        if (!is_array($a_mboxes))
@@ -2619,7 +2707,7 @@
    function create_mailbox($name, $subscribe=false)
    {
        $result = false;
        // reduce mailbox name to 100 chars
        $name = substr($name, 0, 100);
        $abs_name = $this->mod_mailbox($name);
@@ -2650,11 +2738,11 @@
        // make absolute path
        $mailbox = $this->mod_mailbox($mbox_name);
        $abs_name = $this->mod_mailbox($name);
        // check if mailbox is subscribed
        $a_subscribed = $this->_list_mailboxes();
        $subscribed = in_array($mailbox, $a_subscribed);
        // unsubscribe folder
        if ($subscribed)
            $this->conn->unsubscribe($mailbox);
@@ -2664,7 +2752,7 @@
        if ($result) {
            $delm = $this->get_hierarchy_delimiter();
            // check if mailbox children are subscribed
            foreach ($a_subscribed as $c_subscribed)
                if (preg_match('/^'.preg_quote($mailbox.$delm, '/').'/', $c_subscribed)) {
@@ -2675,7 +2763,7 @@
            // clear cache
            $this->clear_message_cache($mailbox.'.msg');
            $this->clear_cache('mailboxes');
            $this->clear_cache('mailboxes');
        }
        // try to subscribe it
@@ -2705,23 +2793,23 @@
            foreach ($a_mboxes as $mbox_name) {
                $mailbox = $this->mod_mailbox($mbox_name);
                $sub_mboxes = $this->conn->listMailboxes($this->mod_mailbox(''),
               $mbox_name . $this->delimiter . '*');
                   $mbox_name . $this->delimiter . '*');
                // unsubscribe mailbox before deleting
                $this->conn->unsubscribe($mailbox);
                // send delete command to server
                $result = $this->conn->deleteFolder($mailbox);
                if ($result >= 0) {
                if ($result) {
                    $deleted = true;
                    $this->clear_message_cache($mailbox.'.msg');
               }
                foreach ($sub_mboxes as $c_mbox) {
                    if ($c_mbox != 'INBOX') {
                        $this->conn->unsubscribe($c_mbox);
                        $result = $this->conn->deleteFolder($c_mbox);
                        if ($result >= 0) {
                        if ($result) {
                            $deleted = true;
                           $this->clear_message_cache($c_mbox.'.msg');
                        }
@@ -2766,14 +2854,19 @@
            if ($mbox_name == 'INBOX')
                return true;
            $key = $subscription ? 'subscribed' : 'existing';
            if (is_array($this->icache[$key]) && in_array($mbox_name, $this->icache[$key]))
                return true;
            if ($subscription) {
                if ($a_folders = $this->conn->listSubscribed($this->mod_mailbox(''), $mbox_name))
                    return true;
                $a_folders = $this->conn->listSubscribed($this->mod_mailbox(''), $mbox_name);
            }
            else {
                $a_folders = $this->conn->listMailboxes($this->mod_mailbox(''), $mbox_mbox);
           if (is_array($a_folders) && in_array($this->mod_mailbox($mbox_name), $a_folders))
                $a_folders = $this->conn->listMailboxes($this->mod_mailbox(''), $mbox_name);
           }
            if (is_array($a_folders) && in_array($this->mod_mailbox($mbox_name), $a_folders)) {
                $this->icache[$key][] = $mbox_name;
                return true;
            }
        }
@@ -2800,7 +2893,7 @@
            else if (!empty($mbox_name)) // $mode=='out'
                $mbox_name = substr($mbox_name, strlen($this->root_dir)+1);
        }
        return $mbox_name;
    }
@@ -2829,7 +2922,7 @@
        if (!count($this->cache) && $this->caching_enabled) {
            return $this->_read_cache_record($key);
        }
        return $this->cache[$key];
    }
@@ -2863,7 +2956,7 @@
    {
        if (!$this->caching_enabled)
            return;
        if ($key===NULL) {
            foreach ($this->cache as $key => $data)
                $this->_clear_cache_record($key);
@@ -2958,7 +3051,7 @@
            "AND cache_key=?",
            $_SESSION['user_id'],
            'IMAP.'.$key);
        unset($this->cache_keys[$key]);
    }
@@ -2967,7 +3060,7 @@
    /* --------------------------------
     *   message caching methods
     * --------------------------------*/
    /**
     * Checks if the cache is up-to-date
     *
@@ -3014,7 +3107,7 @@
                // get UID of message with highest index
                $uid = $this->conn->ID2UID($mailbox, $msg_count);
                $cache_uid = array_pop($cache_index);
                // uids of highest message matches -> cache seems OK
                if ($cache_uid == $uid)
                    return 1;
@@ -3035,12 +3128,12 @@
    private function get_message_cache($key, $from, $to, $sort_field, $sort_order)
    {
        $cache_key = "$key:$from:$to:$sort_field:$sort_order";
        // use idx sort as default sorting
        if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) {
            $sort_field = 'idx';
        }
        if ($this->caching_enabled && !isset($this->cache[$cache_key])) {
            $this->cache[$cache_key] = array();
            $sql_result = $this->db->limitquery(
@@ -3074,7 +3167,7 @@
    private function &get_cached_message($key, $uid)
    {
        $internal_key = 'message';
        if ($this->caching_enabled && !isset($this->icache[$internal_key][$uid])) {
            $sql_result = $this->db->query(
                "SELECT idx, headers, structure".
@@ -3099,22 +3192,22 @@
    /**
     * @access private
     */
     */
    private function get_message_cache_index($key, $force=false, $sort_field='idx', $sort_order='ASC')
    {
        static $sa_message_index = array();
        // empty key -> empty array
        if (!$this->caching_enabled || empty($key))
            return array();
        if (!empty($sa_message_index[$key]) && !$force)
            return $sa_message_index[$key];
        // use idx sort as default
        if (!$sort_field || !in_array($sort_field, $this->db_header_fields))
            $sort_field = 'idx';
        $sa_message_index[$key] = array();
        $sql_result = $this->db->query(
            "SELECT idx, uid".
@@ -3127,7 +3220,7 @@
        while ($sql_arr = $this->db->fetch_assoc($sql_result))
            $sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid'];
        return $sa_message_index[$key];
    }
@@ -3146,8 +3239,8 @@
        // no further caching
        if (!$this->caching_enabled)
            return;
        // check for an existing record (probly headers are cached but structure not)
        // check for an existing record (probably headers are cached but structure not)
        if (!$force) {
            $sql_result = $this->db->query(
                "SELECT message_id".
@@ -3198,7 +3291,7 @@
            );
        }
    }
    /**
     * @access private
     */
@@ -3206,7 +3299,7 @@
    {
        if (!$this->caching_enabled)
            return;
        $this->db->query(
            "DELETE FROM ".get_table_name('messages').
            " WHERE user_id=?".
@@ -3223,7 +3316,7 @@
    {
        if (!$this->caching_enabled)
            return;
        $this->db->query(
            "DELETE FROM ".get_table_name('messages').
            " WHERE user_id=?".
@@ -3239,7 +3332,7 @@
    {
        if (!$this->caching_enabled)
            return;
        if (!empty($uids) && !is_array($uids)) {
            if ($uids == '*' || $uids == '1:*')
                $uids = NULL;
@@ -3259,7 +3352,7 @@
        if ($sql_arr = $this->db->fetch_assoc($sql_result))
            return $sql_arr['minidx'];
        else
            return 0;
            return 0;
    }
@@ -3281,7 +3374,7 @@
        $out = array();
        // Special chars as defined by RFC 822 need to in quoted string (or escaped).
        $special_chars = '[\(\)\<\>\\\.\[\]@,;:"]';
        if (!is_array($a))
            return $out;
@@ -3302,7 +3395,7 @@
                $string = $address;
            else if ($name)
                $string = $name;
            $out[$j] = array('name' => $name,
                'mailto' => $address,
                'string' => $string
@@ -3311,45 +3404,8 @@
            if ($max && $j==$max)
                break;
        }
        return $out;
    }
    /**
     * Decode a Microsoft Outlook TNEF part (winmail.dat)
     *
     * @param object rcube_message_part Message part to decode
     * @param string UID of the message
     * @return array List of rcube_message_parts extracted from windmail.dat
     */
    function tnef_decode(&$part, $uid)
    {
        if (!isset($part->body))
            $part->body = $this->get_message_part($uid, $part->mime_id, $part);
        require_once('lib/tnef_decoder.inc');
        $pid = 0;
        $tnef_parts = array();
        $tnef_arr = tnef_decode($part->body);
        foreach ($tnef_arr as $winatt) {
            $tpart = new rcube_message_part;
            $tpart->filename = $winatt["name"];
            $tpart->encoding = 'stream';
            $tpart->ctype_primary = $winatt["type0"];
            $tpart->ctype_secondary = $winatt["type1"];
            $tpart->mimetype = strtolower($winatt["type0"] . "/" . $winatt["type1"]);
            $tpart->mime_id = "winmail." . $part->mime_id . ".$pid";
            $tpart->size = $winatt["size"];
            $tpart->body = $winatt['stream'];
            $tnef_parts[] = $tpart;
            $pid++;
        }
        return $tnef_parts;
    }
@@ -3365,7 +3421,7 @@
        $str = rcube_imap::decode_mime_string((string)$input, $this->default_charset);
        if ($str{0}=='"' && $remove_quotes)
            $str = str_replace('"', '', $str);
        return $str;
    }
@@ -3385,15 +3441,15 @@
        $out = '';
        // Iterate instead of recursing, this way if there are too many values we don't have stack overflows
        // rfc: all line breaks or other characters not found
        // rfc: all line breaks or other characters not found
        // in the Base64 Alphabet must be ignored by decoding software
        // delete all blanks between MIME-lines, differently we can
        // delete all blanks between MIME-lines, differently we can
        // receive unnecessary blanks and broken utf-8 symbols
        $input = preg_replace("/\?=\s+=\?/", '?==?', $input);
        // Check if there is stuff to decode
        if (strpos($input, '=?') !== false) {
            // Loop through the string to decode all occurences of =? ?= into the variable $out
            // Loop through the string to decode all occurences of =? ?= into the variable $out
            while(($pos = strpos($input, '=?')) !== false) {
                // Append everything that is before the text to be decoded
                $out .= substr($input, 0, $pos);
@@ -3421,7 +3477,7 @@
        }
        // no encoding information, use fallback
        return rcube_charset_convert($input,
        return rcube_charset_convert($input,
            !empty($fallback) ? $fallback : rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1'));
    }
@@ -3451,7 +3507,7 @@
            return rcube_charset_convert($rest, $a[0]);
        }
        // we dont' know what to do with this
        // we dont' know what to do with this
        return $str;
    }
@@ -3533,7 +3589,7 @@
            if (($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p])
                $a_defaults[$p] = $folder;
            else
                $folders[$folder] = mb_strtolower(rcube_charset_convert($folder, 'UTF7-IMAP'));
                $folders[$folder] = rcube_charset_convert($folder, 'UTF7-IMAP');
        }
        // sort folders and place defaults on the top
@@ -3541,15 +3597,15 @@
        ksort($a_defaults);
        $folders = array_merge($a_defaults, array_keys($folders));
        // finally we must rebuild the list to move
        // 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)
            // set the type of folder name variable (#1485527)
            $a_out[] = (string) $folder;
            unset($folders[$key]);
            $this->_rsort($folder, $delimiter, $folders, $a_out);
            $this->_rsort($folder, $delimiter, $folders, $a_out);
        }
        return $a_out;
@@ -3563,13 +3619,13 @@
    {
        while (list($key, $name) = each($list)) {
           if (strpos($name, $folder.$delimiter) === 0) {
               // set the type of folder name variable (#1485527)
               // set the type of folder name variable (#1485527)
               $out[] = (string) $name;
               unset($list[$key]);
               $this->_rsort($name, $delimiter, $list, $out);
           }
        }
        reset($list);
        reset($list);
    }
@@ -3580,7 +3636,7 @@
    {
        if (!$mbox_name)
            $mbox_name = $this->mailbox;
        if (!isset($this->uid_id_map[$mbox_name][$uid]))
            $this->uid_id_map[$mbox_name][$uid] = $this->conn->UID2ID($mbox_name, $uid);
@@ -3600,7 +3656,7 @@
        $uid = $this->conn->ID2UID($mbox_name, $id);
        $this->uid_id_map[$mbox_name][$uid] = $id;
        return $uid;
    }
@@ -3655,20 +3711,20 @@
        $mode = strtoupper($mode);
        $a_mailbox_cache = $this->get_cache('messagecount');
        if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
            return false;
        // add incremental value to messagecount
        $a_mailbox_cache[$mailbox][$mode] += $increment;
        // there's something wrong, delete from cache
        if ($a_mailbox_cache[$mailbox][$mode] < 0)
            unset($a_mailbox_cache[$mailbox][$mode]);
        // write back to cache
        $this->update_cache('messagecount', $a_mailbox_cache);
        return true;
    }
@@ -3710,7 +3766,7 @@
                    $a_headers[$field] = $value;
            }
        }
        return $a_headers;
    }
@@ -3736,13 +3792,13 @@
                else
                    $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
            }
            if (empty($result[$key]['name']))
                $result[$key]['name'] = $result[$key]['address'];
                $result[$key]['name'] = $result[$key]['address'];
            elseif (empty($result[$key]['address']))
                $result[$key]['address'] = $result[$key]['name'];
        }
        return $result;
    }
@@ -3788,7 +3844,7 @@
class rcube_header_sorter
{
    var $sequence_numbers = array();
    /**
     * Set the predetermined sort order.
     *
@@ -3810,12 +3866,12 @@
        * uksort would work if the keys were the sequence number, but unfortunately
        * the keys are the UIDs.  We'll use uasort instead and dereference the value
        * to get the sequence number (in the "id" field).
        *
        * uksort($headers, array($this, "compare_seqnums"));
        *
        * uksort($headers, array($this, "compare_seqnums"));
        */
        uasort($headers, array($this, "compare_seqnums"));
    }
    /**
     * Sort method called by uasort()
     */
@@ -3824,11 +3880,11 @@
        // First get the sequence number from the header object (the 'id' field).
        $seqa = $a->id;
        $seqb = $b->id;
        // then find each sequence number in my ordered list
        $posa = isset($this->sequence_numbers[$seqa]) ? intval($this->sequence_numbers[$seqa]) : -1;
        $posb = isset($this->sequence_numbers[$seqb]) ? intval($this->sequence_numbers[$seqb]) : -1;
        // return the relative position as the comparison value
        return $posa - $posb;
    }