Thomas Bruederli
2013-10-15 b6e24c6946606cd504d522451c36b6dc574fe75d
program/lib/Roundcube/rcube_imap.php
@@ -70,7 +70,7 @@
    protected $search_sort_field = '';
    protected $search_threads = false;
    protected $search_sorted = false;
    protected $options = array('auth_method' => 'check');
    protected $options = array('auth_type' => 'check');
    protected $caching = false;
    protected $messages_caching = false;
    protected $threading = false;
@@ -391,10 +391,10 @@
    public function check_permflag($flag)
    {
        $flag       = strtoupper($flag);
        $imap_flag  = $this->conn->flags[$flag];
        $perm_flags = $this->get_permflags($this->folder);
        $imap_flag  = $this->conn->flags[$flag];
        return in_array_nocase($imap_flag, $perm_flags);
        return $imap_flag && !empty($perm_flags) && in_array_nocase($imap_flag, $perm_flags);
    }
@@ -410,17 +410,7 @@
        if (!strlen($folder)) {
            return array();
        }
/*
        Checking PERMANENTFLAGS is rather rare, so we disable caching of it
        Re-think when we'll use it for more than only MDNSENT flag
        $cache_key = 'mailboxes.permanentflags.' . $folder;
        $permflags = $this->get_cache($cache_key);
        if ($permflags !== null) {
            return explode(' ', $permflags);
        }
*/
        if (!$this->check_connection()) {
            return array();
        }
@@ -435,10 +425,7 @@
        if (!is_array($permflags)) {
            $permflags = array();
        }
/*
        // Store permflags as string to limit cached object size
        $this->update_cache($cache_key, implode(' ', $permflags));
*/
        return $permflags;
    }
@@ -693,6 +680,41 @@
    /**
     * Public method for listing message flags
     *
     * @param string $folder  Folder name
     * @param array  $uids    Message UIDs
     * @param int    $mod_seq Optional MODSEQ value (of last flag update)
     *
     * @return array Indexed array with message flags
     */
    public function list_flags($folder, $uids, $mod_seq = null)
    {
        if (!strlen($folder)) {
            $folder = $this->folder;
        }
        if (!$this->check_connection()) {
            return array();
        }
        // @TODO: when cache was synchronized in this request
        // we might already have asked for flag updates, use it.
        $flags  = $this->conn->fetch($folder, $uids, true, array('FLAGS'), $mod_seq);
        $result = array();
        if (!empty($flags)) {
            foreach ($flags as $message) {
                $result[$message->uid] = $message->flags;
            }
        }
        return $result;
    }
    /**
     * Public method for listing headers
     *
     * @param   string   $folder     Folder name
@@ -923,6 +945,50 @@
            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;
            $this->sort_field = null;
            $this->page_size = 100;  // limit to 100 messages per folder
            $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));
                }
            }
            // do sorting and paging
            $cnt   = $search_set->count();
            $from  = ($page-1) * $page_size;
            $to    = $from + $page_size;
            // sort headers
            if (!$this->threading && !empty($a_msg_headers)) {
                $a_msg_headers = $this->conn->sortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
            }
            // only return the requested part of the set
            $slice_length  = min($page_size, $cnt - ($to > $cnt ? $from : $to));
            $a_msg_headers = array_slice(array_values($a_msg_headers), $from, $slice_length);
            if ($slice) {
                $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice);
            }
            // 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);
@@ -1089,6 +1155,7 @@
        }
        foreach ($headers as $h) {
            $h->folder = $folder;
            $a_msg_headers[$h->uid] = $h;
        }
@@ -1400,11 +1467,31 @@
            $str = 'ALL';
        }
        if (!strlen($folder)) {
        if (empty($folder)) {
            $folder = $this->folder;
        }
        $results = $this->search_index($folder, $str, $charset, $sort_field);
        // multi-folder search
        if (is_array($folder) && count($folder) > 1 && $str != 'ALL') {
            new rcube_result_index; // trigger autoloader and make these classes available for threaded context
            new rcube_result_thread;
            // connect IMAP to have all the required classes and settings loaded
            $this->check_connection();
            $searcher = new rcube_imap_search($this->options, $this->conn);
            $results = $searcher->exec(
                $folder,
                $str,
                $charset ? $charset : $this->default_charset,
                $sort_field && $this->get_capability('SORT') ? $sort_field : null,
                $this->threading
            );
        }
        else {
            $folder = is_array($folder) ? $folder[0] : $folder;
            $results = $this->search_index($folder, $str, $charset, $sort_field);
        }
        $this->set_search_set(array($str, $results, $charset, $sort_field,
            $this->threading || $this->search_sorted ? true : false));
@@ -1422,7 +1509,7 @@
    public function search_once($folder = null, $str = 'ALL')
    {
        if (!$str) {
            return 'ALL';
            $str = 'ALL';
        }
        if (!strlen($folder)) {
@@ -1478,7 +1565,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;
@@ -1492,7 +1579,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()) {
@@ -1507,7 +1594,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;
@@ -1525,7 +1612,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)) {
@@ -2092,17 +2179,18 @@
    /**
     * Fetch message body of a specific message from the server
     *
     * @param  int                $uid    Message UID
     * @param  string             $part   Part number
     * @param  rcube_message_part $o_part Part object created by get_structure()
     * @param  mixed              $print  True to print part, ressource to write part contents in
     * @param  resource           $fp     File pointer to save the message part
     * @param  boolean            $skip_charset_conv Disables charset conversion
     * @param  int                $max_bytes  Only read this number of bytes
     * @param int                Message UID
     * @param string             Part number
     * @param rcube_message_part Part object created by get_structure()
     * @param mixed              True to print part, resource to write part contents in
     * @param resource           File pointer to save the message part
     * @param boolean            Disables charset conversion
     * @param int                Only read this number of bytes
     * @param boolean            Enables formatting of text/* parts bodies
     *
     * @return string Message/part body if not printed
     */
    public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0)
    public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0, $formatted=true)
    {
        if (!$this->check_connection()) {
            return null;
@@ -2121,8 +2209,9 @@
        }
        if ($o_part && $o_part->size) {
            $formatted = $formatted && $o_part->ctype_primary == 'text';
            $body = $this->conn->handlePartBody($this->folder, $uid, true,
                $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $o_part->ctype_primary == 'text', $max_bytes);
                $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $formatted, $max_bytes);
        }
        if ($fp || $print) {
@@ -2132,7 +2221,7 @@
        // convert charset (if text or message part)
        if ($body && preg_match('/^(text|message)$/', $o_part->ctype_primary)) {
            // Remove NULL characters if any (#1486189)
            if (strpos($body, "\x00") !== false) {
            if ($formatted && strpos($body, "\x00") !== false) {
                $body = str_replace("\x00", '', $body);
            }
@@ -2385,7 +2474,7 @@
                    $this->refresh_search();
                }
                else {
                    $this->search_set->filter(explode(',', $uids));
                    $this->search_set->filter(explode(',', $uids), $this->folder);
                }
            }
@@ -2667,7 +2756,6 @@
        if ($list_extended) {
            // unsubscribe non-existent folders, remove from the list
            // we can do this only when LIST response is available
            if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) {
                foreach ($a_folders as $idx => $folder) {
                    if (($opts = $this->conn->data['LIST'][$folder])
@@ -2680,19 +2768,14 @@
            }
        }
        else {
            // unsubscribe non-existent folders, remove them from the list,
            // we can do this only when LIST response is available
            if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) {
                foreach ($a_folders as $idx => $folder) {
                    if (!isset($this->conn->data['LIST'][$folder])
                        || in_array('\\Noselect', $this->conn->data['LIST'][$folder])
                    ) {
                        // Some servers returns \Noselect for existing folders
                        if (!$this->folder_exists($folder)) {
                            $this->conn->unsubscribe($folder);
                            unset($a_folders[$idx]);
                        }
                    }
            // unsubscribe non-existent folders, remove them from the list
            if (is_array($a_folders) && !empty($a_folders) && $name == '*') {
                $existing    = $this->list_folders($root, $name);
                $nonexisting = array_diff($a_folders, $existing);
                $a_folders   = array_diff($a_folders, $nonexisting);
                foreach ($nonexisting as $folder) {
                    $this->conn->unsubscribe($folder);
                }
            }
        }
@@ -2860,12 +2943,21 @@
    /**
     * Filter the given list of folders according to access rights
     *
     * For performance reasons we assume user has full rights
     * on all personal folders.
     */
    protected function filter_rights($a_folders, $rights)
    {
        $regex = '/('.$rights.')/';
        foreach ($a_folders as $idx => $folder) {
            if ($this->folder_namespace($folder) == 'personal') {
                continue;
            }
            $myrights = join('', (array)$this->my_rights($folder));
            if ($myrights !== null && !preg_match($regex, $myrights)) {
                unset($a_folders[$idx]);
            }
@@ -3777,12 +3869,17 @@
    /**
     * Enable or disable messages caching
     *
     * @param boolean $set Flag
     * @param boolean $set  Flag
     * @param int     $mode Cache mode
     */
    public function set_messages_caching($set)
    public function set_messages_caching($set, $mode = null)
    {
        if ($set) {
            $this->messages_caching = true;
            if ($mode && ($cache = $this->get_mcache_engine())) {
                $cache->set_mode($mode);
            }
        }
        else {
            if ($this->mcache) {
@@ -3802,9 +3899,10 @@
        if ($this->messages_caching && !$this->mcache) {
            $rcube = rcube::get_instance();
            if (($dbh = $rcube->get_dbh()) && ($userid = $rcube->get_user_id())) {
                $ttl = $rcube->config->get('messages_cache_ttl', '10d');
                $ttl       = $rcube->config->get('messages_cache_ttl', '10d');
                $threshold = $rcube->config->get('messages_cache_threshold', 50);
                $this->mcache = new rcube_imap_cache(
                    $dbh, $this, $userid, $this->options['skip_deleted'], $ttl);
                    $dbh, $this, $userid, $this->options['skip_deleted'], $ttl, $threshold);
            }
        }
@@ -3816,7 +3914,7 @@
     * Clears the messages cache.
     *
     * @param string $folder Folder name
     * @param array  $uids    Optional message UIDs to remove from cache
     * @param array  $uids   Optional message UIDs to remove from cache
     */
    protected function clear_message_cache($folder = null, $uids = null)
    {
@@ -3859,9 +3957,12 @@
    /**
     * Sort folders first by default folders and then in alphabethical order
     *
     * @param array $a_folders Folders list
     * @param array $a_folders    Folders list
     * @param bool  $skip_default Skip default folders handling
     *
     * @return array Sorted list
     */
    protected function sort_folder_list($a_folders)
    public function sort_folder_list($a_folders, $skip_default = false)
    {
        $a_out = $a_defaults = $folders = array();
@@ -3873,7 +3974,7 @@
                continue;
            }
            if (($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
            if (!$skip_default && ($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
                $a_defaults[$p] = $folder;
            }
            else {