Aleksander Machniak
2013-06-11 545559307e2cf7d986af5993f7d3ea0fc30a8386
program/lib/Roundcube/rcube_imap.php
@@ -2,8 +2,6 @@
/*
 +-----------------------------------------------------------------------+
 | program/include/rcube_imap.php                                        |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2005-2012, The Roundcube Dev Team                       |
 | Copyright (C) 2011-2012, Kolab Systems AG                             |
@@ -14,13 +12,11 @@
 |                                                                       |
 | PURPOSE:                                                              |
 |   IMAP Storage Engine                                                 |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
*/
/**
 * Interface class for accessing an IMAP server
@@ -151,7 +147,7 @@
        $attempt = 0;
        do {
            $data = rcube::get_instance()->plugins->exec_hook('imap_connect',
            $data = rcube::get_instance()->plugins->exec_hook('storage_connect',
                array_merge($this->options, array('host' => $host, 'user' => $user,
                    'attempt' => ++$attempt)));
@@ -571,7 +567,7 @@
     * Get message count for a specific folder
     *
     * @param  string  $folder  Folder name
     * @param  string  $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT]
     * @param  string  $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT|EXISTS]
     * @param  boolean $force   Force reading from server and update cache
     * @param  boolean $status  Enables storing folder status info (max UID/count),
     *                          required for folder_status()
@@ -592,7 +588,7 @@
     * protected method for getting nr of messages
     *
     * @param string  $folder  Folder name
     * @param string  $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT]
     * @param string  $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT|EXISTS]
     * @param boolean $force   Force reading from server and update cache
     * @param boolean $status  Enables storing folder status info (max UID/count),
     *                         required for folder_status()
@@ -613,6 +609,10 @@
                return $this->search_set->count();
            }
        }
        // EXISTS is a special alias for ALL, it allows to get the number
        // of all messages in a folder also when search is active and with
        // any skip_deleted setting
        $a_folder_cache = $this->get_cache('messagecount');
@@ -644,7 +644,7 @@
            $count = $this->conn->countRecent($folder);
        }
        // use SEARCH for message counting
        else if (!empty($this->options['skip_deleted'])) {
        else if ($mode != 'EXISTS' && !empty($this->options['skip_deleted'])) {
            $search_str = "ALL UNDELETED";
            $keys       = array('COUNT');
@@ -683,8 +683,8 @@
            }
            else {
                $count = $this->conn->countMessages($folder);
                if ($status) {
                    $this->set_folder_stats($folder,'cnt', $count);
                if ($status && $mode == 'ALL') {
                    $this->set_folder_stats($folder, 'cnt', $count);
                    $this->set_folder_stats($folder, 'maxuid', $count ? $this->id2uid($count, $folder) : 0);
                }
            }
@@ -812,20 +812,22 @@
            return $mcache->get_thread($folder);
        }
        if (empty($this->icache['threads'])) {
            if (!$this->check_connection()) {
                return new rcube_result_thread();
        if (!empty($this->icache['threads'])) {
            if ($this->icache['threads']->get_parameters('MAILBOX') == $folder) {
                return $this->icache['threads'];
            }
            // get all threads
            $result = $this->conn->thread($folder, $this->threading,
                $this->options['skip_deleted'] ? 'UNDELETED' : '', true);
            // add to internal (fast) cache
            $this->icache['threads'] = $result;
        }
        return $this->icache['threads'];
        if (!$this->check_connection()) {
            return new rcube_result_thread();
        }
        // get all threads
        $result = $this->conn->thread($folder, $this->threading,
            $this->options['skip_deleted'] ? 'UNDELETED' : '', true);
        // add to internal (fast) cache
        return $this->icache['threads'] = $result;
    }
@@ -981,7 +983,7 @@
            // use memory less expensive (and quick) method for big result set
            $index = clone $this->index('', $this->sort_field, $this->sort_order);
            // get messages uids for one page...
            $index->slice($start_msg, min($cnt-$from, $this->page_size));
            $index->slice($from, min($cnt-$from, $this->page_size));
            if ($slice) {
                $index->slice(-$slice, $slice);
@@ -1096,16 +1098,17 @@
    /**
     * Returns current status of folder
     * Returns current status of a folder (compared to the last time use)
     *
     * We compare the maximum UID to determine the number of
     * new messages because the RECENT flag is not reliable.
     *
     * @param string $folder Folder name
     * @param array  $diff   Difference data
     *
     * @return int   Folder status
     * @return int Folder status
     */
    public function folder_status($folder = null)
    public function folder_status($folder = null, &$diff = array())
    {
        if (!strlen($folder)) {
            $folder = $this->folder;
@@ -1126,6 +1129,9 @@
        // got new messages
        if ($new['maxuid'] > $old['maxuid']) {
            $result += 1;
            // get new message UIDs range, that can be used for example
            // to get the data of these messages
            $diff['new'] = ($old['maxuid'] + 1 < $new['maxuid'] ? ($old['maxuid']+1).':' : '') . $new['maxuid'];
        }
        // some messages has been deleted
        if ($new['cnt'] < $old['cnt']) {
@@ -1419,8 +1425,6 @@
     */
    protected function search_index($folder, $criteria='ALL', $charset=NULL, $sort_field=NULL)
    {
        $orig_criteria = $criteria;
        if (!$this->check_connection()) {
            if ($this->threading) {
                return new rcube_result_thread();
@@ -1634,9 +1638,15 @@
        // Example of structure for malformed MIME message:
        // ("text" "plain" NIL NIL NIL "7bit" 2154 70 NIL NIL NIL)
        if ($headers->ctype && !is_array($structure[0]) && $headers->ctype != 'text/plain'
            && strtolower($structure[0].'/'.$structure[1]) == 'text/plain') {
            && strtolower($structure[0].'/'.$structure[1]) == 'text/plain'
        ) {
            // A special known case "Content-type: text" (#1488968)
            if ($headers->ctype == 'text') {
                $structure[1]   = 'plain';
                $headers->ctype = 'text/plain';
            }
            // we can handle single-part messages, by simple fix in structure (#1486898)
            if (preg_match('/^(text|application)\/(.*)/', $headers->ctype, $m)) {
            else if (preg_match('/^(text|application)\/(.*)/', $headers->ctype, $m)) {
                $structure[0] = $m[1];
                $structure[1] = $m[2];
            }
@@ -1660,11 +1670,21 @@
            $struct = $this->structure_part($structure, 0, '', $headers);
        }
        // don't trust given content-type
        if (empty($struct->parts) && !empty($headers->ctype)) {
            $struct->mime_id = '1';
            $struct->mimetype = strtolower($headers->ctype);
            list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype);
        // some workarounds on simple messages...
        if (empty($struct->parts)) {
            // ...don't trust given content-type
            if (!empty($headers->ctype)) {
                $struct->mime_id  = '1';
                $struct->mimetype = strtolower($headers->ctype);
                list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype);
            }
            // ...and charset (there's a case described in #1488968 where invalid content-type
            // results in invalid charset in BODYSTRUCTURE)
            if (!empty($headers->charset) && $headers->charset != $struct->ctype_parameters['charset']) {
                $struct->charset                     = $headers->charset;
                $struct->ctype_parameters['charset'] = $headers->charset;
            }
        }
        $headers->structure = $struct;
@@ -2317,10 +2337,7 @@
        // move messages
        $moved = $this->conn->move($uids, $from_mbox, $to_mbox);
        // send expunge command in order to have the moved message
        // really deleted from the source folder
        if ($moved) {
            $this->expunge_message($uids, $from_mbox, false);
            $this->clear_messagecount($from_mbox);
            $this->clear_messagecount($to_mbox);
        }
@@ -2710,7 +2727,7 @@
        // filter folders list according to rights requirements
        if ($rights && $this->get_capability('ACL')) {
            $a_folders = $this->filter_rights($a_folders, $rights);
            $a_mboxes = $this->filter_rights($a_mboxes, $rights);
        }
        // filter folders and sort them
@@ -2766,7 +2783,6 @@
     */
    private function list_folders_update(&$result, $type = null)
    {
        $delim     = $this->get_hierarchy_delimiter();
        $namespace = $this->get_namespace();
        $search    = array();
@@ -3355,7 +3371,6 @@
    {
        if (!empty($this->options['fetch_headers'])) {
            $headers = explode(' ', $this->options['fetch_headers']);
            $headers = array_map('strtoupper', $headers);
        }
        else {
            $headers = array();
@@ -3365,7 +3380,7 @@
            $headers = array_merge($headers, $this->all_headers);
        }
        return implode(' ', array_unique($headers));
        return $headers;
    }
@@ -3678,7 +3693,7 @@
    {
        if ($this->caching && !$this->cache) {
            $rcube = rcube::get_instance();
            $ttl = $rcube->config->get('message_cache_lifetime', '10d');
            $ttl   = $rcube->config->get('imap_cache_ttl', '10d');
            $this->cache = $rcube->get_cache('IMAP', $this->caching, $ttl);
        }
@@ -3726,21 +3741,6 @@
        }
    }
    /**
     * Delete outdated cache entries
     */
    public function expunge_cache()
    {
        if ($this->mcache) {
            $ttl = rcube::get_instance()->config->get('message_cache_lifetime', '10d');
            $this->mcache->expunge($ttl);
        }
        if ($this->cache) {
            $this->cache->expunge();
        }
    }
    /* --------------------------------
     *   message caching methods
@@ -3774,8 +3774,9 @@
        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');
                $this->mcache = new rcube_imap_cache(
                    $dbh, $this, $userid, $this->options['skip_deleted']);
                    $dbh, $this, $userid, $this->options['skip_deleted'], $ttl);
            }
        }
@@ -3794,6 +3795,15 @@
        if ($mcache = $this->get_mcache_engine()) {
            $mcache->clear($folder, $uids);
        }
    }
    /**
     * Delete outdated cache entries
     */
    function cache_gc()
    {
        rcube_imap_cache::gc();
    }
@@ -3830,7 +3840,7 @@
        $delimiter = $this->get_hierarchy_delimiter();
        // find default folders and skip folders starting with '.'
        foreach ($a_folders as $i => $folder) {
        foreach ($a_folders as $folder) {
            if ($folder[0] == '.') {
                continue;
            }