Aleksander Machniak
2015-06-17 3f4521bcf4b538b6ac54817cfad22b51e347546d
program/lib/Roundcube/rcube_imap_cache.php
@@ -125,6 +125,11 @@
        // cache all possible information by default
        $this->mode = self::MODE_INDEX | self::MODE_MESSAGE;
        // database tables
        $this->index_table    = $db->table_name('cache_index', true);
        $this->thread_table   = $db->table_name('cache_thread', true);
        $this->messages_table = $db->table_name('cache_messages', true);
    }
@@ -171,7 +176,7 @@
        // Seek in internal cache
        if (array_key_exists('index', $this->icache[$mailbox])) {
            // The index was fetched from database already, but not validated yet
            if (!array_key_exists('object', $this->icache[$mailbox]['index'])) {
            if (empty($this->icache[$mailbox]['index']['validated'])) {
                $index = $this->icache[$mailbox]['index'];
            }
            // We've got a valid index
@@ -248,6 +253,7 @@
        }
        $this->icache[$mailbox]['index'] = array(
            'validated'  => true,
            'object'     => $data,
            'sort_field' => $sort_field,
            'modseq'     => !empty($index['modseq']) ? $index['modseq'] : $mbox_data['HIGHESTMODSEQ']
@@ -332,11 +338,11 @@
        if ($this->mode & self::MODE_MESSAGE) {
            // Fetch messages from cache
            $sql_result = $this->db->query(
                "SELECT uid, data, flags"
                ." FROM ".$this->db->table_name('cache_messages')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?"
                    ." AND uid IN (".$this->db->array2list($msgs, 'integer').")",
                "SELECT `uid`, `data`, `flags`"
                ." FROM {$this->messages_table}"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?"
                    ." AND `uid` IN (".$this->db->array2list($msgs, 'integer').")",
                $this->userid, $mailbox);
            $msgs = array_flip($msgs);
@@ -399,11 +405,11 @@
        if ($this->mode & self::MODE_MESSAGE) {
            $sql_result = $this->db->query(
                "SELECT flags, data"
                ." FROM ".$this->db->table_name('cache_messages')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?"
                    ." AND uid = ?",
                "SELECT `flags`, `data`"
                ." FROM {$this->messages_table}"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?"
                    ." AND `uid` = ?",
                    $this->userid, $mailbox, (int)$uid);
            if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -479,11 +485,11 @@
        // here will work as select, assume row exist if affected_rows=0)
        if (!$force) {
            $res = $this->db->query(
                "UPDATE ".$this->db->table_name('cache_messages')
                ." SET flags = ?, data = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?"
                    ." AND uid = ?",
                "UPDATE {$this->messages_table}"
                ." SET `flags` = ?, `data` = ?, `expires` = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?"
                    ." AND `uid` = ?",
                $flags, $msg, $this->userid, $mailbox, (int) $message->uid);
            if ($this->db->affected_rows($res)) {
@@ -495,8 +501,8 @@
        // insert new record
        $res = $this->db->query(
            "INSERT INTO ".$this->db->table_name('cache_messages')
            ." (user_id, mailbox, uid, flags, expires, data)"
            "INSERT INTO {$this->messages_table}"
            ." (`user_id`, `mailbox`, `uid`, `flags`, `expires`, `data`)"
            ." VALUES (?, ?, ?, ?, ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?)",
            $this->userid, $mailbox, (int) $message->uid, $flags, $msg);
@@ -504,12 +510,12 @@
        // thanks to ignore_key_errors "duplicate row" errors will be ignored
        if ($force && !$res && !$this->db->is_error($res)) {
            $this->db->query(
                "UPDATE ".$this->db->table_name('cache_messages')
                ." SET expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                    .", flags = ?, data = ?"
                ." WHERE user_id = ?"
                    ." AND mailbox = ?"
                    ." AND uid = ?",
                "UPDATE {$this->messages_table}"
                ." SET `expires` = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                    .", `flags` = ?, `data` = ?"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?"
                    ." AND `uid` = ?",
                $flags, $msg, $this->userid, $mailbox, (int) $message->uid);
        }
@@ -556,14 +562,16 @@
            }
        }
        $binary_check = $this->db->db_provider == 'oracle' ? "BITAND(`flags`, %d)" : "(`flags` & %d)";
        $this->db->query(
            "UPDATE ".$this->db->table_name('cache_messages')
            ." SET expires = ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
            .", flags = flags ".($enabled ? "+ $idx" : "- $idx")
            ." WHERE user_id = ?"
                ." AND mailbox = ?"
                .(!empty($uids) ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "")
                ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"),
            "UPDATE {$this->messages_table}"
            ." SET `expires` = ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
            .", `flags` = `flags` ".($enabled ? "+ $idx" : "- $idx")
            ." WHERE `user_id` = ?"
                ." AND `mailbox` = ?"
                .(!empty($uids) ? " AND `uid` IN (".$this->db->array2list($uids, 'integer').")" : "")
                ." AND " . sprintf($binary_check, $idx) . ($enabled ? " = 0" : " = $idx"),
            $this->userid, $mailbox);
    }
@@ -582,8 +590,8 @@
        if (!strlen($mailbox)) {
            $this->db->query(
                "DELETE FROM ".$this->db->table_name('cache_messages')
                ." WHERE user_id = ?",
                "DELETE FROM {$this->messages_table}"
                ." WHERE `user_id` = ?",
                $this->userid);
        }
        else {
@@ -596,10 +604,10 @@
            }
            $this->db->query(
                "DELETE FROM ".$this->db->table_name('cache_messages')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?"
                    .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : ""),
                "DELETE FROM {$this->messages_table}"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?"
                    .($uids !== null ? " AND `uid` IN (".$this->db->array2list((array)$uids, 'integer').")" : ""),
                $this->userid, $mailbox);
        }
    }
@@ -618,18 +626,18 @@
        // otherwise use 'valid' flag to not loose HIGHESTMODSEQ value
        if ($remove) {
            $this->db->query(
                "DELETE FROM ".$this->db->table_name('cache_index')
                ." WHERE user_id = ?"
                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""),
                "DELETE FROM {$this->index_table}"
                ." WHERE `user_id` = ?"
                    .(strlen($mailbox) ? " AND `mailbox` = ".$this->db->quote($mailbox) : ""),
                $this->userid
            );
        }
        else {
            $this->db->query(
                "UPDATE ".$this->db->table_name('cache_index')
                ." SET valid = 0"
                ." WHERE user_id = ?"
                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""),
                "UPDATE {$this->index_table}"
                ." SET `valid` = 0"
                ." WHERE `user_id` = ?"
                    .(strlen($mailbox) ? " AND `mailbox` = ".$this->db->quote($mailbox) : ""),
                $this->userid
            );
        }
@@ -653,9 +661,9 @@
    function remove_thread($mailbox = null)
    {
        $this->db->query(
            "DELETE FROM ".$this->db->table_name('cache_thread')
            ." WHERE user_id = ?"
                .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""),
            "DELETE FROM {$this->thread_table}"
            ." WHERE `user_id` = ?"
                .(strlen($mailbox) ? " AND `mailbox` = ".$this->db->quote($mailbox) : ""),
            $this->userid
        );
@@ -691,15 +699,16 @@
    {
        $rcube = rcube::get_instance();
        $db    = $rcube->get_dbh();
        $now   = $db->now();
        $db->query("DELETE FROM ".$db->table_name('cache_messages')
              ." WHERE expires < " . $db->now());
        $db->query("DELETE FROM " . $db->table_name('cache_messages', true)
              ." WHERE `expires` < $now");
        $db->query("DELETE FROM ".$db->table_name('cache_index')
              ." WHERE expires < " . $db->now());
        $db->query("DELETE FROM " . $db->table_name('cache_index', true)
              ." WHERE `expires` < $now");
        $db->query("DELETE FROM ".$db->table_name('cache_thread')
              ." WHERE expires < " . $db->now());
        $db->query("DELETE FROM ".$db->table_name('cache_thread', true)
              ." WHERE `expires` < $now");
    }
@@ -710,10 +719,10 @@
    {
        // Get index from DB
        $sql_result = $this->db->query(
            "SELECT data, valid"
            ." FROM ".$this->db->table_name('cache_index')
            ." WHERE user_id = ?"
                ." AND mailbox = ?",
            "SELECT `data`, `valid`"
            ." FROM {$this->index_table}"
            ." WHERE `user_id` = ?"
                ." AND `mailbox` = ?",
            $this->userid, $mailbox);
        if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -747,10 +756,10 @@
    {
        // Get thread from DB
        $sql_result = $this->db->query(
            "SELECT data"
            ." FROM ".$this->db->table_name('cache_thread')
            ." WHERE user_id = ?"
                ." AND mailbox = ?",
            "SELECT `data`"
            ." FROM {$this->thread_table}"
            ." WHERE `user_id` = ?"
                ." AND `mailbox` = ?",
            $this->userid, $mailbox);
        if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -788,14 +797,16 @@
            (int) $mbox_data['UIDNEXT'],
            $modseq ? $modseq : $mbox_data['HIGHESTMODSEQ'],
        );
        $data = implode('@', $data);
        $data    = implode('@', $data);
        $expires = $this->ttl ? $this->db->now($this->ttl) : 'NULL';
        if ($exists) {
            $res = $this->db->query(
                "UPDATE ".$this->db->table_name('cache_index')
                ." SET data = ?, valid = 1, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?",
                "UPDATE {$this->index_table}"
                ." SET `data` = ?, `valid` = 1, `expires` = $expires"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?",
                $data, $this->userid, $mailbox);
            if ($this->db->affected_rows($res)) {
@@ -806,19 +817,19 @@
        $this->db->set_option('ignore_key_errors', true);
        $res = $this->db->query(
            "INSERT INTO ".$this->db->table_name('cache_index')
            ." (user_id, mailbox, valid, expires, data)"
            ." VALUES (?, ?, 1, ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') .", ?)",
            "INSERT INTO {$this->index_table}"
            ." (`user_id`, `mailbox`, `valid`, `expires`, `data`)"
            ." VALUES (?, ?, 1, $expires, ?)",
            $this->userid, $mailbox, $data);
        // race-condition, insert failed so try update (#1489146)
        // thanks to ignore_key_errors "duplicate row" errors will be ignored
        if (!$exists && !$res && !$this->db->is_error($res)) {
            $res = $this->db->query(
                "UPDATE ".$this->db->table_name('cache_index')
                ." SET data = ?, valid = 1, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?",
                "UPDATE {$this->index_table}"
                ." SET `data` = ?, `valid` = 1, `expires` = $expires"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?",
                $data, $this->userid, $mailbox);
        }
@@ -837,16 +848,16 @@
            (int) $mbox_data['UIDVALIDITY'],
            (int) $mbox_data['UIDNEXT'],
        );
        $data = implode('@', $data);
        $expires = ($this->ttl ? $this->db->now($this->ttl) : 'NULL');
        $data    = implode('@', $data);
        $expires = $this->ttl ? $this->db->now($this->ttl) : 'NULL';
        if ($exists) {
            $res = $this->db->query(
                "UPDATE ".$this->db->table_name('cache_thread')
                ." SET data = ?, expires = $expires"
                ." WHERE user_id = ?"
                    ." AND mailbox = ?",
                "UPDATE {$this->thread_table}"
                ." SET `data` = ?, `expires` = $expires"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?",
                $data, $this->userid, $mailbox);
            if ($this->db->affected_rows($res)) {
@@ -857,8 +868,8 @@
        $this->db->set_option('ignore_key_errors', true);
        $res = $this->db->query(
            "INSERT INTO ".$this->db->table_name('cache_thread')
            ." (user_id, mailbox, expires, data)"
            "INSERT INTO {$this->thread_table}"
            ." (`user_id`, `mailbox`, `expires`, `data`)"
            ." VALUES (?, ?, $expires, ?)",
            $this->userid, $mailbox, $data);
@@ -866,10 +877,10 @@
        // thanks to ignore_key_errors "duplicate row" errors will be ignored
        if (!$exists && !$res && !$this->db->is_error($res)) {
            $this->db->query(
                "UPDATE ".$this->db->table_name('cache_thread')
                ." SET expires = $expires, data = ?"
                ." WHERE user_id = ?"
                    ." AND mailbox = ?",
                "UPDATE {$this->thread_table}"
                ." SET `expires` = $expires, `data` = ?"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?",
                $data, $this->userid, $mailbox);
        }
@@ -889,6 +900,8 @@
        if (empty($object)) {
            return false;
        }
        $index['validated'] = true;
        // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
        $mbox_data = $this->imap->folder_data($mailbox);
@@ -1083,10 +1096,10 @@
        // Get known UIDs
        if ($this->mode & self::MODE_MESSAGE) {
            $sql_result = $this->db->query(
                "SELECT uid"
                ." FROM ".$this->db->table_name('cache_messages')
                ." WHERE user_id = ?"
                    ." AND mailbox = ?",
                "SELECT `uid`"
                ." FROM {$this->messages_table}"
                ." WHERE `user_id` = ?"
                    ." AND `mailbox` = ?",
                $this->userid, $mailbox);
            while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -1122,12 +1135,12 @@
                    }
                    $this->db->query(
                        "UPDATE ".$this->db->table_name('cache_messages')
                        ." SET flags = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                        ." WHERE user_id = ?"
                            ." AND mailbox = ?"
                            ." AND uid = ?"
                            ." AND flags <> ?",
                        "UPDATE {$this->messages_table}"
                        ." SET `flags` = ?, `expires` = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL')
                        ." WHERE `user_id` = ?"
                            ." AND `mailbox` = ?"
                            ." AND `uid` = ?"
                            ." AND `flags` <> ?",
                        $flags, $this->userid, $mailbox, $uid, $flags);
                }
            }
@@ -1232,12 +1245,14 @@
    private function message_object_prepare(&$msg, &$size = 0)
    {
        // Remove body too big
        if ($msg->body && ($length = strlen($msg->body))) {
            $size += $length;
        if (isset($msg->body)) {
            $length = strlen($msg->body);
            if ($size > $this->threshold * 1024) {
                $size -= $length;
            if ($msg->body_modified || $size + $length > $this->threshold * 1024) {
                unset($msg->body);
            }
            else {
                $size += $length;
            }
        }
@@ -1250,10 +1265,8 @@
        unset($msg->replaces);
        if (is_array($msg->structure->parts)) {
            foreach ($msg->structure->parts as $part) {
                $this->message_object_prepare($part, $size);
            }
        if (is_object($msg->structure)) {
            $this->message_object_prepare($msg->structure, $size);
        }
        if (is_array($msg->parts)) {