function get_message($mailbox, $uid, $update = true, $cache = true)
    {
        // Check internal cache
        if ($this->icache['__message']
            && $this->icache['__message']['mailbox'] == $mailbox
            && $this->icache['__message']['object']->uid == $uid
        ) {
            return $this->icache['__message']['object'];
        }

        if ($this->mode & self::MODE_MESSAGE) {
            $sql_result = $this->db->query(
                "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)) {
                $message = $this->build_message($sql_arr);
                $found   = true;
            }
        }

        // Get the message from IMAP server
        if (empty($message) && $update) {
            $message = $this->imap->get_message_headers($uid, $mailbox, true);
            // cache will be updated in close(), see below
        }

        if (!($this->mode & self::MODE_MESSAGE)) {
            return $message;
        }

        // Save the message in internal cache, will be written to DB in close()
        // Common scenario: user opens unseen message
        // - get message (SELECT)
        // - set message headers/structure (INSERT or UPDATE)
        // - set \Seen flag (UPDATE)
        // This way we can skip one UPDATE
        if (!empty($message) && $cache) {
            // Save current message from internal cache
            $this->save_icache();

            $this->icache['__message'] = array(
                'object'  => $message,
                'mailbox' => $mailbox,
                'exists'  => $found,
                'md5sum'  => md5(serialize($message)),
            );
        }

        return $message;
    }