Aleksander Machniak
2015-09-02 427ab2f3938122d7ce1d0862a583a5adaed6c6c9
program/lib/Roundcube/rcube_imap.php
@@ -57,7 +57,6 @@
    protected $icache = array();
    protected $plugins;
    protected $list_page = 1;
    protected $delimiter;
    protected $namespace;
    protected $sort_field = '';
@@ -82,7 +81,7 @@
     */
    public function __construct()
    {
        $this->conn = new rcube_imap_generic();
        $this->conn    = new rcube_imap_generic();
        $this->plugins = rcube::get_instance()->plugins;
        // Set namespace and delimiter from session,
@@ -470,7 +469,7 @@
            return;
        }
        $config = rcube::get_instance()->config;
        $config         = rcube::get_instance()->config;
        $imap_personal  = $config->get('imap_ns_personal');
        $imap_other     = $config->get('imap_ns_other');
        $imap_shared    = $config->get('imap_ns_shared');
@@ -542,6 +541,52 @@
    }
    /**
     * Returns IMAP server vendor name
     *
     * @return string Vendor name
     * @since 1.2
     */
    public function get_vendor()
    {
        if ($_SESSION['imap_vendor'] !== null) {
            return $_SESSION['imap_vendor'];
        }
        $config      = rcube::get_instance()->config;
        $imap_vendor = $config->get('imap_vendor');
        if ($imap_vendor) {
            return $imap_vendor;
        }
        if (!$this->check_connection()) {
            return;
        }
        if (($ident = $this->conn->data['ID']) === null) {
            $ident = $this->conn->id(array(
                    'name'    => 'Roundcube',
                    'version' => RCUBE_VERSION,
                    'php'     => PHP_VERSION,
                    'os'      => PHP_OS,
            ));
        }
        $vendor  = (string) (!empty($ident) ? $ident['name'] : '');
        $ident   = strtolower($vendor . ' ' . $this->conn->data['GREETING']);
        $vendors = array('cyrus', 'dovecot', 'uw-imap', 'gmail', 'hmail');
        foreach ($vendors as $v) {
            if (strpos($ident, $v) !== false) {
                $vendor = $v;
                break;
            }
        }
        return $_SESSION['imap_vendor'] = $vendor;
    }
    /**
     * Get message count for a specific folder
     *
     * @param  string  $folder  Folder name
@@ -562,27 +607,30 @@
    }
    /**
     * protected method for getting nr of messages
     * Protected method for getting number of messages
     *
     * @param string  $folder  Folder name
     * @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()
     * @param string  $folder    Folder name
     * @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()
     * @param boolean $no_search Ignore current search result
     *
     * @return int Number of messages
     * @see rcube_imap::count()
     */
    protected function countmessages($folder, $mode='ALL', $force=false, $status=true)
    protected function countmessages($folder, $mode = 'ALL', $force = false, $status = true, $no_search = false)
    {
        $mode = strtoupper($mode);
        // count search set, assume search set is always up-to-date (don't check $force flag)
        if ($this->search_string && $folder == $this->folder && ($mode == 'ALL' || $mode == 'THREADS')) {
        // Count search set, assume search set is always up-to-date (don't check $force flag)
        // @TODO: this could be handled in more reliable way, e.g. a separate method
        //        maybe in rcube_imap_search
        if (!$no_search && $this->search_string && $folder == $this->folder) {
            if ($mode == 'ALL') {
                return $this->search_set->count_messages();
            }
            else {
            else if ($mode == 'THREADS') {
                return $this->search_set->count();
            }
        }
@@ -1200,7 +1248,7 @@
        $old = $this->get_folder_stats($folder);
        // refresh message count -> will update
        $this->countmessages($folder, 'ALL', true);
        $this->countmessages($folder, 'ALL', true, true, true);
        $result = 0;
@@ -2379,7 +2427,7 @@
     *
     * @param mixed   $uids       Message UIDs as array or comma-separated string, or '*'
     * @param string  $flag       Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
     * @param string  $folder    Folder name
     * @param string  $folder     Folder name
     * @param boolean $skip_cache True to skip message cache clean up
     *
     * @return boolean  Operation status
@@ -2804,42 +2852,47 @@
        $list_extended = !$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED');
        if ($list_extended) {
            // This will also set folder options, LSUB doesn't do that
            $a_folders = $this->conn->listMailboxes($root, $name,
            $result = $this->conn->listMailboxes($root, $name,
                NULL, array('SUBSCRIBED'));
        }
        else {
            // retrieve list of folders from IMAP server using LSUB
            $a_folders = $this->conn->listSubscribed($root, $name);
            $result = $this->conn->listSubscribed($root, $name);
        }
        if (!is_array($a_folders)) {
        if (!is_array($result)) {
            return array();
        }
        // #1486796: some server configurations doesn't return folders in all namespaces
        if ($root == '' && $name == '*' && $config->get('imap_force_ns')) {
            $this->list_folders_update($a_folders, ($list_extended ? 'ext-' : '') . 'subscribed');
            $this->list_folders_update($result, ($list_extended ? 'ext-' : '') . 'subscribed');
        }
        // Remove hidden folders
        if ($config->get('imap_skip_hidden_folders')) {
            $result = array_filter($result, function($v) { return $v[0] != '.'; });
        }
        if ($list_extended) {
            // unsubscribe non-existent folders, remove from the list
            if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) {
                foreach ($a_folders as $idx => $folder) {
            if ($name == '*' && !empty($this->conn->data['LIST'])) {
                foreach ($result as $idx => $folder) {
                    if (($opts = $this->conn->data['LIST'][$folder])
                        && in_array_nocase('\\NonExistent', $opts)
                    ) {
                        $this->conn->unsubscribe($folder);
                        unset($a_folders[$idx]);
                        unset($result[$idx]);
                    }
                }
            }
        }
        else {
            // unsubscribe non-existent folders, remove them from the list
            if (is_array($a_folders) && !empty($a_folders) && $name == '*') {
            if (!empty($result) && $name == '*') {
                $existing    = $this->list_folders($root, $name);
                $nonexisting = array_diff($a_folders, $existing);
                $a_folders   = array_diff($a_folders, $nonexisting);
                $nonexisting = array_diff($result, $existing);
                $result      = array_diff($result, $nonexisting);
                foreach ($nonexisting as $folder) {
                    $this->conn->unsubscribe($folder);
@@ -2847,7 +2900,7 @@
            }
        }
        return $a_folders;
        return $result;
    }
    /**
@@ -2944,6 +2997,11 @@
        // #1486796: some server configurations doesn't return folders in all namespaces
        if ($root == '' && $name == '*' && $config->get('imap_force_ns')) {
            $this->list_folders_update($result);
        }
        // Remove hidden folders
        if ($config->get('imap_skip_hidden_folders')) {
            $result = array_filter($result, function($v) { return $v[0] != '.'; });
        }
        return $result;
@@ -3054,8 +3112,22 @@
     */
    public function folder_size($folder)
    {
        if (!strlen($folder)) {
            return false;
        }
        if (!$this->check_connection()) {
            return 0;
        }
        // On Cyrus we can use special folder annotation, which should be much faster
        if ($this->get_vendor() == 'cyrus') {
            $idx    = '/shared/vendor/cmu/cyrus-imapd/size';
            $result = $this->get_metadata($folder, $idx, array(), true);
            if (!empty($result) && is_numeric($result[$folder][$idx])) {
                return $result[$folder][$idx];
            }
        }
        // @TODO: could we try to use QUOTA here?
@@ -3110,16 +3182,6 @@
        }
        $result = $this->conn->createFolder($folder, $type ? array("\\" . ucfirst($type)) : null);
        // it's quite often situation that we're trying to create and subscribe
        // a folder that already exist, but is unsubscribed
        if (!$result) {
            if ($this->get_response_code() == rcube_storage::ALREADYEXISTS
                || preg_match('/already exists/i', $this->get_error_str())
            ) {
                $result = true;
            }
        }
        // try to subscribe it
        if ($result) {
@@ -3831,30 +3893,33 @@
    /**
     * Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION)
     *
     * @param string $folder  Folder name (empty for server metadata)
     * @param array  $entries Entries
     * @param array  $options Command options (with MAXSIZE and DEPTH keys)
     * @param string  $folder   Folder name (empty for server metadata)
     * @param array   $entries  Entries
     * @param array   $options  Command options (with MAXSIZE and DEPTH keys)
     * @param bool    $force    Disables cache use
     *
     * @return array Metadata entry-value hash array on success, NULL on error
     * @since 0.5-beta
     */
    public function get_metadata($folder, $entries, $options=array())
    public function get_metadata($folder, $entries, $options = array(), $force = false)
    {
        $entries = (array)$entries;
        $entries = (array) $entries;
        // create cache key
        // @TODO: this is the simplest solution, but we do the same with folders list
        //        maybe we should store data per-entry and merge on request
        sort($options);
        sort($entries);
        $cache_key = 'mailboxes.metadata.' . $folder;
        $cache_key .= '.' . md5(serialize($options).serialize($entries));
        if (!$force) {
            // create cache key
            // @TODO: this is the simplest solution, but we do the same with folders list
            //        maybe we should store data per-entry and merge on request
            sort($options);
            sort($entries);
            $cache_key = 'mailboxes.metadata.' . $folder;
            $cache_key .= '.' . md5(serialize($options).serialize($entries));
        // get cached data
        $cached_data = $this->get_cache($cache_key);
            // get cached data
            $cached_data = $this->get_cache($cache_key);
        if (is_array($cached_data)) {
            return $cached_data;
            if (is_array($cached_data)) {
                return $cached_data;
            }
        }
        if (!$this->check_connection()) {
@@ -3893,7 +3958,10 @@
        }
        if (isset($res)) {
            $this->update_cache($cache_key, $res);
            if (!$force) {
                $this->update_cache($cache_key, $res);
            }
            return $res;
        }
@@ -4102,13 +4170,11 @@
        $specials  = array_merge(array('INBOX'), array_values($this->get_special_folders()));
        $folders   = array();
        // convert names to UTF-8 and skip folders starting with '.'
        // convert names to UTF-8
        foreach ($a_folders as $folder) {
            if ($folder[0] != '.') {
                // for better performance skip encoding conversion
                // if the string does not look like UTF7-IMAP
                $folders[$folder] = strpos($folder, '&') === false ? $folder : rcube_charset::convert($folder, 'UTF7-IMAP');
            }
            // for better performance skip encoding conversion
            // if the string does not look like UTF7-IMAP
            $folders[$folder] = strpos($folder, '&') === false ? $folder : rcube_charset::convert($folder, 'UTF7-IMAP');
        }
        // sort folders