Aleksander Machniak
2015-08-08 559e5d7bd40e5c4f8575e4abb78d8e30ec1bc77e
program/lib/Roundcube/rcube_imap_generic.php
@@ -48,8 +48,6 @@
        '*'        => '\\*',
    );
    public static $mupdate;
    protected $fp;
    protected $host;
    protected $logged = false;
@@ -554,14 +552,12 @@
                $this->putLine($reply, true, true);
                $line = trim($this->readReply());
                if ($line[0] == '+') {
                    $challenge = substr($line, 2);
                }
                else {
                if ($line[0] != '+') {
                    return $this->parseResult($line);
                }
                // check response
                $challenge = substr($line, 2);
                $challenge = base64_decode($challenge);
                if (strpos($challenge, 'rspauth=') === false) {
                    $this->setError(self::ERROR_BAD,
@@ -573,6 +569,66 @@
            }
            $line = $this->readReply();
            $result = $this->parseResult($line);
        }
        elseif ($type == 'GSSAPI') {
            if (!extension_loaded('krb5')) {
                $this->setError(self::ERROR_BYE,
                    "The krb5 extension is required for GSSAPI authentication");
                return self::ERROR_BAD;
            }
            if (empty($this->prefs['gssapi_cn'])) {
                $this->setError(self::ERROR_BYE,
                    "The gssapi_cn parameter is required for GSSAPI authentication");
                return self::ERROR_BAD;
            }
            if (empty($this->prefs['gssapi_context'])) {
                $this->setError(self::ERROR_BYE,
                    "The gssapi_context parameter is required for GSSAPI authentication");
                return self::ERROR_BAD;
            }
            putenv('KRB5CCNAME=' . $this->prefs['gssapi_cn']);
            try {
                $ccache = new KRB5CCache();
                $ccache->open($this->prefs['gssapi_cn']);
                $gssapicontext = new GSSAPIContext();
                $gssapicontext->acquireCredentials($ccache);
                $token   = '';
                $success = $gssapicontext->initSecContext($this->prefs['gssapi_context'], null, null, null, $token);
                $token   = base64_encode($token);
            }
            catch (Exception $e) {
                trigger_error($e->getMessage(), E_USER_WARNING);
                $this->setError(self::ERROR_BYE, "GSSAPI authentication failed");
                return self::ERROR_BAD;
            }
            $this->putLine($this->nextTag() . " AUTHENTICATE GSSAPI " . $token);
            $line = trim($this->readReply());
            if ($line[0] != '+') {
                return $this->parseResult($line);
            }
            try {
                $challenge = base64_decode(substr($line, 2));
                $gssapicontext->unwrap($challenge, $challenge);
                $gssapicontext->wrap($challenge, $challenge, true);
            }
            catch (Exception $e) {
                trigger_error($e->getMessage(), E_USER_WARNING);
                $this->setError(self::ERROR_BYE, "GSSAPI authentication failed");
                return self::ERROR_BAD;
            }
            $this->putLine(base64_encode($challenge));
            $line   = $this->readReply();
            $result = $this->parseResult($line);
        }
        else { // PLAIN
@@ -692,7 +748,8 @@
        list($code, $response) = $this->execute('NAMESPACE');
        if ($code == self::ERROR_OK && preg_match('/^\* NAMESPACE /', $response)) {
            $data = $this->tokenizeResponse(substr($response, 11));
            $response = substr($response, 11);
            $data     = $this->tokenizeResponse($response);
        }
        if (!is_array($data)) {
@@ -739,7 +796,7 @@
            return false;
        }
        if (empty($password)) {
        if (empty($password) && empty($options['gssapi_cn'])) {
            $this->setError(self::ERROR_NO, "Empty password");
            return false;
        }
@@ -775,7 +832,8 @@
            }
            // Use best (for security) supported authentication method
            foreach (array('DIGEST-MD5', 'CRAM-MD5', 'CRAM_MD5', 'PLAIN', 'LOGIN') as $auth_method) {
            $all_methods = array('GSSAPI', 'DIGEST-MD5', 'CRAM-MD5', 'CRAM_MD5', 'PLAIN', 'LOGIN');
            foreach ($all_methods as $auth_method) {
                if (in_array($auth_method, $auth_methods)) {
                    break;
                }
@@ -804,6 +862,7 @@
            case 'CRAM-MD5':
            case 'DIGEST-MD5':
            case 'PLAIN':
            case 'GSSAPI':
                $result = $this->authenticate($user, $password, $auth_method);
                break;
            case 'LOGIN':
@@ -1108,7 +1167,8 @@
            // folder name with spaces. Let's try to handle this situation
            if (!is_array($items) && ($pos = strpos($response, '(')) !== false) {
                $response = substr($response, $pos);
                $items = $this->tokenizeResponse($response, 1);
                $items    = $this->tokenizeResponse($response, 1);
                if (!is_array($items)) {
                    return $result;
                }
@@ -1146,7 +1206,7 @@
        }
        // Clear internal status cache
        unset($this->data['STATUS:'.$mailbox]);
        $this->clear_status_cache($mailbox);
        if (!empty($messages) && $messages != '*' && $this->hasCapability('UIDPLUS')) {
            $messages = self::compressMessageSet($messages);
@@ -1461,13 +1521,9 @@
     *
     * @return int Number of messages, False on error
     */
    function countMessages($mailbox, $refresh = false)
    function countMessages($mailbox)
    {
        if ($refresh) {
            $this->selected = null;
        }
        if ($this->selected === $mailbox) {
        if ($this->selected === $mailbox && isset($this->data['EXISTS'])) {
            return $this->data['EXISTS'];
        }
@@ -1495,14 +1551,20 @@
     */
    function countRecent($mailbox)
    {
        if (!strlen($mailbox)) {
            $mailbox = 'INBOX';
        if ($this->selected === $mailbox && isset($this->data['RECENT'])) {
            return $this->data['RECENT'];
        }
        $this->select($mailbox);
        // Check internal cache
        $cache = $this->data['STATUS:'.$mailbox];
        if (!empty($cache) && isset($cache['RECENT'])) {
            return (int) $cache['RECENT'];
        }
        if ($this->selected === $mailbox) {
            return $this->data['RECENT'];
        // Try STATUS (should be faster than SELECT)
        $counts = $this->status($mailbox, array('RECENT'));
        if (is_array($counts)) {
            return (int) $counts['RECENT'];
        }
        return false;
@@ -1704,7 +1766,6 @@
        $encoding  = $encoding ? trim($encoding) : 'US-ASCII';
        $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES';
        $criteria  = $criteria ? 'ALL '.trim($criteria) : 'ALL';
        $data      = '';
        list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD',
            array($algorithm, $encoding, $criteria));
@@ -2124,7 +2185,7 @@
            // Clear internal status cache
            unset($this->data['STATUS:'.$to]);
            unset($this->data['STATUS:'.$from]);
            $this->clear_status_cache($from);
            $result = $this->execute('UID MOVE', array(
                $this->compressMessageSet($messages), $this->escape($to)),
@@ -2438,7 +2499,16 @@
        return false;
    }
    function sortHeaders($a, $field, $flag)
    /**
     * Sort messages by specified header field
     *
     * @param array  $messages Array of rcube_message_header objects
     * @param string $field    Name of the property to sort by
     * @param string $flag     Sorting order (ASC|DESC)
     *
     * @return array Sorted input array
     */
    public static function sortHeaders($messages, $field, $flag)
    {
        if (empty($field)) {
            $field = 'uid';
@@ -2447,57 +2517,65 @@
            $field = strtolower($field);
        }
        if ($field == 'date' || $field == 'internaldate') {
            $field = 'timestamp';
        }
        if (empty($flag)) {
            $flag = 'ASC';
        } else {
        }
        else {
            $flag = strtoupper($flag);
        }
        $c = count($a);
        if ($c > 0) {
            // Strategy:
            // First, we'll create an "index" array.
            // Then, we'll use sort() on that array,
            // and use that to sort the main array.
        // Strategy: First, we'll create an "index" array.
        // Then, we'll use sort() on that array, and use that to sort the main array.
            // create "index" array
            $index = array();
            reset($a);
            while (list($key, $val) = each($a)) {
                if ($field == 'timestamp') {
                    $data = $this->strToTime($val->date);
                    if (!$data) {
                        $data = $val->timestamp;
                    }
                } else {
                    $data = $val->$field;
                    if (is_string($data)) {
                        $data = str_replace('"', '', $data);
                        if ($field == 'subject') {
                            $data = preg_replace('/^(Re: \s*|Fwd:\s*|Fw:\s*)+/i', '', $data);
                        }
                        $data = strtoupper($data);
                    }
        $index  = array();
        $result = array();
        reset($messages);
        while (list($key, $headers) = each($messages)) {
            $value = null;
            switch ($field) {
            case 'arrival':
                $field = 'internaldate';
            case 'date':
            case 'internaldate':
            case 'timestamp':
                $value = self::strToTime($headers->$field);
                if (!$value && $field != 'timestamp') {
                    $value = $headers->timestamp;
                }
                $index[$key] = $data;
                break;
            default:
                // @TODO: decode header value, convert to UTF-8
                $value = $headers->$field;
                if (is_string($value)) {
                    $value = str_replace('"', '', $value);
                    if ($field == 'subject') {
                        $value = preg_replace('/^(Re:\s*|Fwd:\s*|Fw:\s*)+/i', '', $value);
                    }
                    $data = strtoupper($value);
                }
            }
            $index[$key] = $value;
        }
        if (!empty($index)) {
            // sort index
            if ($flag == 'ASC') {
                asort($index);
            } else {
            }
            else {
                arsort($index);
            }
            // form new array based on index
            $result = array();
            reset($index);
            while (list($key, $val) = each($index)) {
                $result[$key] = $a[$key];
                $result[$key] = $messages[$key];
            }
        }
@@ -3271,11 +3349,6 @@
        }
        foreach ($data as $entry) {
            // Workaround cyrus-murder bug, the entry[2] string needs to be escaped
            if (self::$mupdate) {
                $entry[2] = addcslashes($entry[2], '\\"');
            }
            // ANNOTATEMORE drafts before version 08 require quoted parameters
            $entries[] = sprintf('%s (%s %s)', $this->escape($entry[0], true),
                $this->escape($entry[1], true), $this->escape($entry[2], true));
@@ -3768,6 +3841,17 @@
    }
    /**
     * Clear internal status cache
     */
    protected function clear_status_cache($mailbox)
    {
        unset($this->data['STATUS:' . $mailbox]);
        unset($this->data['EXISTS']);
        unset($this->data['RECENT']);
        unset($this->data['UNSEEN']);
    }
    /**
     * Converts flags array into string for inclusion in IMAP command
     *
     * @param array $flags Flags (see self::flags)
@@ -3838,10 +3922,6 @@
        if (!isset($this->prefs['literal+']) && in_array('LITERAL+', $this->capability)) {
            $this->prefs['literal+'] = true;
        }
        if (preg_match('/(\[| )MUPDATE=.*/', $str)) {
            self::$mupdate = true;
        }
        if ($trusted) {