Changeset 5233 in subversion


Ignore:
Timestamp:
Sep 18, 2011 5:02:35 AM (20 months ago)
Author:
alec
Message:
  • Cache synchronization using QRESYNC/CONDSTORE
  • Fixed message ID updates in cache
  • Changed message flags handling + some fixes (e.g. fixed messages listing after delete)
Location:
trunk/roundcubemail
Files:
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail

  • trunk/roundcubemail/CHANGELOG

    r5232 r5233  
    22=========================== 
    33 
     4- Cache synchronization using QRESYNC/CONDSTORE 
    45- Fix locked folder rename option on servers supporting RFC2086 only (#1488089) 
    56- Trigger 'new_messages' hook for all checked folders (#1488083) 
     
    1819  Added threads data caching 
    1920  Flags are stored separately, so flag change doesn't cause DELETE+INSERT, just UPDATE 
    20 - Partial QRESYNC support 
    2121- Improved FETCH response handling 
    2222- Improvements in response tokenization method 
  • trunk/roundcubemail/SQL/mssql.initial.sql

    r5190 r5233  
    1212        [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , 
    1313        [changed] [datetime] NOT NULL , 
     14        [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , 
    1415        [data] [text] COLLATE Latin1_General_CI_AI NOT NULL  
    1516) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 
     
    3031        [changed] [datetime] NOT NULL , 
    3132        [data] [text] COLLATE Latin1_General_CI_AI NOT NULL  
    32         [seen] [char](1) NOT NULL , 
    33         [deleted] [char](1) NOT NULL , 
    34         [answered] [char](1) NOT NULL , 
    35         [forwarded] [char](1) NOT NULL , 
    36         [flagged] [char](1) NOT NULL , 
    37         [mdnsent] [char](1) NOT NULL , 
     33        [flags] [int](1) NOT NULL , 
    3834) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 
    3935GO 
     
    230226ALTER TABLE [dbo].[cache_messages] ADD  
    231227        CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed] 
    232         CONSTRAINT [DF_cache_messages_seen] DEFAULT (0) FOR [seen], 
    233         CONSTRAINT [DF_cache_messages_deleted] DEFAULT (0) FOR [deleted], 
    234         CONSTRAINT [DF_cache_messages_answered] DEFAULT (0) FOR [answered], 
    235         CONSTRAINT [DF_cache_messages_forwarded] DEFAULT (0) FOR [forwarded], 
    236         CONSTRAINT [DF_cache_messages_flagged] DEFAULT (0) FOR [flagged], 
    237         CONSTRAINT [DF_cache_messages_mdnsent] DEFAULT (0) FOR [mdnsent], 
     228        CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags], 
    238229GO 
    239230 
  • trunk/roundcubemail/SQL/mssql.upgrade.sql

    r5190 r5233  
    176176        [changed] [datetime] NOT NULL , 
    177177        [data] [text] COLLATE Latin1_General_CI_AI NOT NULL  
    178         [seen] [char](1) NOT NULL , 
    179         [deleted] [char](1) NOT NULL , 
    180         [answered] [char](1) NOT NULL , 
    181         [forwarded] [char](1) NOT NULL , 
    182         [flagged] [char](1) NOT NULL , 
    183         [mdnsent] [char](1) NOT NULL , 
     178        [flags] [int] NOT NULL , 
    184179) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 
    185180GO 
     
    221216 
    222217ALTER TABLE [dbo].[cache_messages] ADD  
    223         CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed] 
    224         CONSTRAINT [DF_cache_messages_seen] DEFAULT (0) FOR [seen], 
    225         CONSTRAINT [DF_cache_messages_deleted] DEFAULT (0) FOR [deleted], 
    226         CONSTRAINT [DF_cache_messages_answered] DEFAULT (0) FOR [answered], 
    227         CONSTRAINT [DF_cache_messages_forwarded] DEFAULT (0) FOR [forwarded], 
    228         CONSTRAINT [DF_cache_messages_flagged] DEFAULT (0) FOR [flagged], 
    229         CONSTRAINT [DF_cache_messages_mdnsent] DEFAULT (0) FOR [mdnsent], 
     218        CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed], 
     219        CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags] 
    230220GO 
    231221 
  • trunk/roundcubemail/SQL/mysql.initial.sql

    r5190 r5233  
    5656 `mailbox` varchar(255) BINARY NOT NULL, 
    5757 `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', 
     58 `valid` tinyint(1) NOT NULL DEFAULT '0', 
    5859 `data` longtext NOT NULL, 
    5960 CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`) 
     
    8687 `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', 
    8788 `data` longtext NOT NULL, 
    88  `seen` tinyint(1) NOT NULL DEFAULT '0', 
    89  `deleted` tinyint(1) NOT NULL DEFAULT '0', 
    90  `answered` tinyint(1) NOT NULL DEFAULT '0', 
    91  `forwarded` tinyint(1) NOT NULL DEFAULT '0', 
    92  `flagged` tinyint(1) NOT NULL DEFAULT '0', 
    93  `mdnsent` tinyint(1) NOT NULL DEFAULT '0', 
     89 `flags` int(11) NOT NULL DEFAULT '0', 
    9490 CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`) 
    9591   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, 
  • trunk/roundcubemail/SQL/mysql.update.sql

    r5190 r5233  
    202202 `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', 
    203203 `data` longtext NOT NULL, 
    204  `seen` tinyint(1) NOT NULL DEFAULT '0', 
    205  `deleted` tinyint(1) NOT NULL DEFAULT '0', 
    206  `answered` tinyint(1) NOT NULL DEFAULT '0', 
    207  `forwarded` tinyint(1) NOT NULL DEFAULT '0', 
    208  `flagged` tinyint(1) NOT NULL DEFAULT '0', 
    209  `mdnsent` tinyint(1) NOT NULL DEFAULT '0', 
     204 `flags` int(11) NOT NULL DEFAULT '0', 
    210205 CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`) 
    211206   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, 
  • trunk/roundcubemail/SQL/postgres.initial.sql

    r5190 r5233  
    198198    mailbox varchar(255) NOT NULL, 
    199199    changed timestamp with time zone DEFAULT now() NOT NULL, 
     200    valid smallint NOT NULL DEFAULT 0, 
    200201    data text NOT NULL, 
    201202    PRIMARY KEY (user_id, mailbox) 
     
    232233    changed timestamp with time zone DEFAULT now() NOT NULL, 
    233234    data text NOT NULL, 
    234     seen smallint NOT NULL DEFAULT 0, 
    235     deleted smallint NOT NULL DEFAULT 0, 
    236     answered smallint NOT NULL DEFAULT 0, 
    237     forwarded smallint NOT NULL DEFAULT 0, 
    238     flagged smallint NOT NULL DEFAULT 0, 
    239     mdnsent smallint NOT NULL DEFAULT 0, 
     235    flags integer NOT NULL DEFAULT 0, 
    240236    PRIMARY KEY (user_id, mailbox, uid) 
    241237); 
  • trunk/roundcubemail/SQL/postgres.update.sql

    r5190 r5233  
    160160    changed timestamp with time zone DEFAULT now() NOT NULL, 
    161161    data text NOT NULL, 
    162     seen smallint NOT NULL DEFAULT 0, 
    163     deleted smallint NOT NULL DEFAULT 0, 
    164     answered smallint NOT NULL DEFAULT 0, 
    165     forwarded smallint NOT NULL DEFAULT 0, 
    166     flagged smallint NOT NULL DEFAULT 0, 
    167     mdnsent smallint NOT NULL DEFAULT 0, 
     162    flags integer NOT NULL DEFAULT 0, 
    168163    PRIMARY KEY (user_id, mailbox, uid) 
    169164); 
  • trunk/roundcubemail/SQL/sqlite.initial.sql

    r5190 r5233  
    160160    mailbox varchar(255) NOT NULL, 
    161161    changed datetime NOT NULL default '0000-00-00 00:00:00', 
     162    valid smallint NOT NULL DEFAULT '0', 
    162163    data text NOT NULL, 
    163164    PRIMARY KEY (user_id, mailbox) 
     
    194195    changed datetime NOT NULL default '0000-00-00 00:00:00', 
    195196    data text NOT NULL, 
    196     seen smallint NOT NULL DEFAULT '0', 
    197     deleted smallint NOT NULL DEFAULT '0', 
    198     answered smallint NOT NULL DEFAULT '0', 
    199     forwarded smallint NOT NULL DEFAULT '0', 
    200     flagged smallint NOT NULL DEFAULT '0', 
    201     mdnsent smallint NOT NULL DEFAULT '0', 
     197    flags integer NOT NULL DEFAULT '0', 
    202198    PRIMARY KEY (user_id, mailbox, uid) 
    203199); 
  • trunk/roundcubemail/SQL/sqlite.update.sql

    r5190 r5233  
    277277    changed datetime NOT NULL default '0000-00-00 00:00:00', 
    278278    data text NOT NULL, 
    279     seen smallint NOT NULL DEFAULT '0', 
    280     deleted smallint NOT NULL DEFAULT '0', 
    281     answered smallint NOT NULL DEFAULT '0', 
    282     forwarded smallint NOT NULL DEFAULT '0', 
    283     flagged smallint NOT NULL DEFAULT '0', 
    284     mdnsent smallint NOT NULL DEFAULT '0', 
     279    flags integer NOT NULL DEFAULT '0', 
    285280    PRIMARY KEY (user_id, mailbox, uid) 
    286281); 
  • trunk/roundcubemail/program/include/rcube_imap.php

    r5232 r5233  
    814814        } 
    815815 
    816         return $this->_list_headers($mailbox, $page, $sort_field, $sort_order, false, $slice); 
     816        return $this->_list_headers($mailbox, $page, $sort_field, $sort_order, $slice); 
    817817    } 
    818818 
     
    10871087            if (!empty($parents)) { 
    10881088                $headers[$idx]->parent_uid = end($parents); 
    1089                 if (!$header->seen) 
     1089                if (empty($header->flags['SEEN'])) 
    10901090                    $headers[$parents[0]]->unread_children++; 
    10911091            } 
     
    34223422            if ($this->conn->select($mailbox)) 
    34233423                $this->mailbox = $mailbox; 
     3424            else 
     3425                return null; 
    34243426        } 
    34253427 
     
    35143516 
    35153517        return $options; 
     3518    } 
     3519 
     3520 
     3521    /** 
     3522     * Synchronizes messages cache. 
     3523     * 
     3524     * @param string $mailbox Folder name 
     3525     */ 
     3526    public function mailbox_sync($mailbox) 
     3527    { 
     3528        if ($mcache = $this->get_mcache_engine()) { 
     3529            $mcache->synchronize($mailbox); 
     3530        } 
    35163531    } 
    35173532 
  • trunk/roundcubemail/program/include/rcube_imap_cache.php

    r5192 r5233  
    6262    private $skip_deleted = false; 
    6363 
    64     public $flag_fields = array('seen', 'deleted', 'answered', 'forwarded', 'flagged', 'mdnsent'); 
     64    /** 
     65     * List of known flags. Thanks to this we can handle flag changes 
     66     * with good performance. Bad thing is we need to know used flags. 
     67     */ 
     68    public $flags = array( 
     69        1       => 'SEEN',          // RFC3501 
     70        2       => 'DELETED',       // RFC3501 
     71        4       => 'ANSWERED',      // RFC3501 
     72        8       => 'FLAGGED',       // RFC3501 
     73        16      => 'DRAFT',         // RFC3501 
     74        32      => 'MDNSENT',       // RFC3503 
     75        64      => 'FORWARDED',     // RFC5550 
     76        128     => 'SUBMITPENDING', // RFC5550 
     77        256     => 'SUBMITTED',     // RFC5550 
     78        512     => 'JUNK', 
     79        1024    => 'NONJUNK', 
     80        2048    => 'LABEL1', 
     81        4096    => 'LABEL2', 
     82        8192    => 'LABEL3', 
     83        16384   => 'LABEL4', 
     84        32768   => 'LABEL5', 
     85    ); 
    6586 
    6687 
     
    106127 
    107128        // Seek in internal cache 
    108         if (array_key_exists('index', $this->icache[$mailbox]) 
    109             && ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field) 
    110         ) { 
    111             if ($this->icache[$mailbox]['index']['sort_order'] == $sort_order) 
    112                 return $this->icache[$mailbox]['index']['result']; 
    113             else 
    114                 return array_reverse($this->icache[$mailbox]['index']['result'], true); 
     129        if (array_key_exists('index', $this->icache[$mailbox])) { 
     130            // The index was fetched from database already, but not validated yet 
     131            if (!array_key_exists('result', $this->icache[$mailbox]['index'])) { 
     132                $index = $this->icache[$mailbox]['index']; 
     133            } 
     134            // We've got a valid index 
     135            else if ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field 
     136            ) { 
     137                if ($this->icache[$mailbox]['index']['sort_order'] == $sort_order) 
     138                    return $this->icache[$mailbox]['index']['result']; 
     139                else 
     140                    return array_reverse($this->icache[$mailbox]['index']['result'], true); 
     141            } 
    115142        } 
    116143 
    117144        // Get index from DB (if DB wasn't already queried) 
    118         if (empty($this->icache[$mailbox]['index_queried'])) { 
     145        if (empty($index) && empty($this->icache[$mailbox]['index_queried'])) { 
    119146            $index = $this->get_index_row($mailbox); 
    120147 
     
    124151            $this->icache[$mailbox]['index_queried'] = true; 
    125152        } 
    126         $data  = null; 
     153 
     154        $data = null; 
    127155 
    128156        // @TODO: Think about skipping validation checks. 
     
    132160        // and many rcube_imap changes to connect when needed 
    133161 
    134         // Entry exist, check cache status 
     162        // Entry exists, check cache status 
    135163        if (!empty($index)) { 
    136164            $exists = true; 
     
    156184        } 
    157185        else { 
     186            if ($existing) { 
     187                return null; 
     188            } 
     189            else if ($sort_field == 'ANY') { 
     190                $sort_field = ''; 
     191            } 
     192 
    158193            // Got it in internal cache, so the row already exist 
    159194            $exists = array_key_exists('index', $this->icache[$mailbox]); 
    160  
    161             if ($existing) { 
    162                 return null; 
    163             } 
    164             else if ($sort_field == 'ANY') { 
    165                 $sort_field = ''; 
    166             } 
    167195        } 
    168196 
     
    171199            // Get mailbox data (UIDVALIDITY, counters, etc.) for status check 
    172200            $mbox_data = $this->imap->mailbox_data($mailbox); 
    173             $data      = array(); 
    174  
    175             // Prevent infinite loop. 
    176             // It happens when rcube_imap::message_index_direct() is called. 
    177             // There id2uid() is called which will again call get_index() and so on. 
    178             if (!$sort_field && !$this->skip_deleted) 
    179                 $this->icache['pending_index_update'] = true; 
    180  
    181             if ($mbox_data['EXISTS']) { 
    182                 // fetch sorted sequence numbers 
    183                 $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); 
    184                 // fetch UIDs 
    185                 if (!empty($data_seq)) { 
    186                     // Seek in internal cache 
    187                     if (array_key_exists('index', (array)$this->icache[$mailbox])) 
    188                         $data_uid = $this->icache[$mailbox]['index']['result']; 
    189                     else 
    190                         $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq); 
    191  
    192                     // build index 
    193                     if (!empty($data_uid)) { 
    194                         foreach ($data_seq as $seq) 
    195                             if ($uid = $data_uid[$seq]) 
    196                                 $data[$seq] = $uid; 
    197                     } 
    198                 } 
    199             } 
    200  
    201             // Reset internal flags 
    202             $this->icache['pending_index_update'] = false; 
     201            $data      = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data); 
    203202 
    204203            // insert/update 
    205             $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, $exists); 
     204            $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, 
     205                $exists, $index['modseq']); 
    206206        } 
    207207 
     
    210210            'sort_field' => $sort_field, 
    211211            'sort_order' => $sort_order, 
     212            'modseq'     => !empty($index['modseq']) ? $index['modseq'] : $mbox_data['HIGHESTMODSEQ'] 
    212213        ); 
    213214 
     
    240241        } 
    241242 
    242         // Get index from DB 
    243         $index = $this->get_thread_row($mailbox); 
    244         $data  = null; 
     243        // Get thread from DB (if DB wasn't already queried) 
     244        if (empty($this->icache[$mailbox]['thread_queried'])) { 
     245            $index = $this->get_thread_row($mailbox); 
     246 
     247            // set the flag that DB was already queried for thread 
     248            // this way we'll be able to skip one SELECT, when 
     249            // get_thread() is called more than once or after clear() 
     250            $this->icache[$mailbox]['thread_queried'] = true; 
     251        } 
     252 
     253        $data = null; 
    245254 
    246255        // Entry exist, check cache status 
     
    295304        } 
    296305 
     306        // @TODO: it would be nice if we could work with UIDs only 
     307        // then index would be not needed. For now we need it to 
     308        // map id to uid here and to update message id for cached message 
     309 
    297310        // Convert IDs to UIDs 
    298         // @TODO: it would be nice if we could work with UIDs only 
    299         // then, e.g. when fetching search result, index would be not needed 
     311        $index = $this->get_index($mailbox, 'ANY'); 
    300312        if (!$is_uid) { 
    301             $index = $this->get_index($mailbox, 'ANY'); 
    302313            foreach ($msgs as $idx => $msgid) 
    303314                if ($uid = $index[$msgid]) 
     
    305316        } 
    306317 
    307         $flag_fields = implode(', ', array_map(array($this->db, 'quoteIdentifier'), $this->flag_fields)); 
    308  
    309318        // Fetch messages from cache 
    310319        $sql_result = $this->db->query( 
    311             "SELECT uid, data, ".$flag_fields 
     320            "SELECT uid, data, flags" 
    312321            ." FROM ".get_table_name('cache_messages') 
    313322            ." WHERE user_id = ?" 
     
    322331            $uid          = intval($sql_arr['uid']); 
    323332            $result[$uid] = $this->build_message($sql_arr); 
    324             // save memory, we don't need a body here 
     333 
     334            // save memory, we don't need message body here (?) 
    325335            $result[$uid]->body = null; 
    326 //@TODO: update message ID according to index data? 
     336 
     337            // update message ID according to index data 
     338            if (!empty($index) && ($id = array_search($uid, $index))) 
     339                $result[$uid]->id = $id; 
    327340 
    328341            if (!empty($result[$uid])) { 
     
    353366     * @param string $mailbox  Folder name 
    354367     * @param int    $uid      Message UID 
     368     * @param bool   $update   If message doesn't exists in cache it will be fetched 
     369     *                         from IMAP server 
     370     * @param bool   $no_cache Enables internal cache usage 
    355371     * 
    356372     * @return rcube_mail_header Message data 
    357373     */ 
    358     function get_message($mailbox, $uid) 
     374    function get_message($mailbox, $uid, $update = true, $cache = true) 
    359375    { 
    360376        // Check internal cache 
     
    365381        } 
    366382 
    367         $flag_fields = implode(', ', array_map(array($this->db, 'quoteIdentifier'), $this->flag_fields)); 
    368  
    369383        $sql_result = $this->db->query( 
    370             "SELECT data, ".$flag_fields 
     384            "SELECT flags, data" 
    371385            ." FROM ".get_table_name('cache_messages') 
    372386            ." WHERE user_id = ?" 
     
    379393            $found   = true; 
    380394 
    381 //@TODO: update message ID according to index data? 
     395            // update message ID according to index data 
     396            $index = $this->get_index($mailbox, 'ANY'); 
     397            if (!empty($index) && ($id = array_search($uid, $index))) 
     398                $message->id = $id; 
    382399        } 
    383400 
    384401        // Get the message from IMAP server 
    385         if (empty($message)) { 
     402        if (empty($message) && $update) { 
    386403            $message = $this->imap->get_headers($uid, $mailbox, true); 
    387404            // cache will be updated in close(), see below 
     
    394411        // - set \Seen flag (UPDATE) 
    395412        // This way we can skip one UPDATE 
    396         if (!empty($message)) { 
     413        if (!empty($message) && $cache) { 
    397414            // Save current message from internal cache 
    398415            $this->save_icache(); 
     
    422439            return; 
    423440 
    424         $msg = serialize($this->db->encode(clone $message)); 
    425  
    426         $flag_fields = array_map(array($this->db, 'quoteIdentifier'), $this->flag_fields); 
    427         $flag_values = array(); 
    428  
    429         foreach ($this->flag_fields as $flag) 
    430             $flag_values[] = (int) $message->$flag; 
     441        $msg   = serialize($this->db->encode(clone $message)); 
     442        $flags = 0; 
     443 
     444        if (!empty($message->flags)) { 
     445            foreach ($this->flags as $idx => $flag) 
     446                if (!empty($message->flags[$flag])) 
     447                    $flags += $idx; 
     448        } 
     449        unset($msg->flags); 
    431450 
    432451        // update cache record (even if it exists, the update 
    433452        // here will work as select, assume row exist if affected_rows=0) 
    434453        if (!$force) { 
    435             foreach ($flag_fields as $key => $val) 
    436                 $flag_data[] = $val . " = " . $flag_values[$key]; 
    437  
    438454            $res = $this->db->query( 
    439455                "UPDATE ".get_table_name('cache_messages') 
    440                 ." SET data = ?, changed = ".$this->db->now() 
    441                 .", " . implode(', ', $flag_data) 
     456                ." SET flags = ?, data = ?, changed = ".$this->db->now() 
    442457                ." WHERE user_id = ?" 
    443458                    ." AND mailbox = ?" 
    444459                    ." AND uid = ?", 
    445                 $msg, $this->userid, $mailbox, (int) $message->uid); 
     460                $flags, $msg, $this->userid, $mailbox, (int) $message->uid); 
    446461 
    447462            if ($this->db->affected_rows()) 
     
    452467        $this->db->query( 
    453468            "INSERT INTO ".get_table_name('cache_messages') 
    454             ." (user_id, mailbox, uid, changed, data, " . implode(', ', $flag_fields) . ")" 
    455             ." VALUES (?, ?, ?, ".$this->db->now().", ?, " . implode(', ', $flag_values) . ")", 
    456             $this->userid, $mailbox, (int) $message->uid, $msg); 
     469            ." (user_id, mailbox, uid, flags, changed, data)" 
     470            ." VALUES (?, ?, ?, ?, ".$this->db->now().", ?)", 
     471            $this->userid, $mailbox, (int) $message->uid, $flags, $msg); 
    457472    } 
    458473 
     
    469484    function change_flag($mailbox, $uids, $flag, $enabled = false) 
    470485    { 
    471         $flag = strtolower($flag); 
    472  
    473         if (in_array($flag, $this->flag_fields)) { 
    474             // Internal cache update 
    475             if ($uids && count($uids) == 1 && ($uid = current($uids)) 
    476                 && ($message = $this->icache['message']) 
    477                 && $message['mailbox'] == $mailbox && $message['object']->uid == $uid 
    478             ) { 
    479                 $message['object']->$flag = $enabled; 
    480                 return; 
    481             } 
    482  
    483             $this->db->query( 
    484                 "UPDATE ".get_table_name('cache_messages') 
    485                 ." SET changed = ".$this->db->now() 
    486                 .", " .$this->db->quoteIdentifier($flag) . " = " . intval($enabled) 
    487                 ." WHERE user_id = ?" 
    488                     ." AND mailbox = ?" 
    489                     .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : ""), 
    490                 $this->userid, $mailbox); 
    491         } 
    492         else { 
    493             // @TODO: SELECT+UPDATE? 
    494             $this->remove_message($mailbox, $uids); 
    495         } 
     486        $flag = strtoupper($flag); 
     487        $idx  = (int) array_search($flag, $this->flags); 
     488 
     489        if (!$idx) { 
     490            return; 
     491        } 
     492 
     493        // Internal cache update 
     494        if ($uids && count($uids) == 1 && ($uid = current($uids)) 
     495            && ($message = $this->icache['message']) 
     496            && $message['mailbox'] == $mailbox && $message['object']->uid == $uid 
     497        ) { 
     498            $message['object']->flags[$flag] = $enabled; 
     499            return; 
     500        } 
     501 
     502        $this->db->query( 
     503            "UPDATE ".get_table_name('cache_messages') 
     504            ." SET changed = ".$this->db->now() 
     505            .", flags = flags ".($enabled ? "+ $idx" : "- $idx") 
     506            ." WHERE user_id = ?" 
     507                ." AND mailbox = ?" 
     508                .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : "") 
     509                ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"), 
     510            $this->userid, $mailbox); 
    496511    } 
    497512 
     
    534549     * 
    535550     * @param string  $mailbox     Folder name 
    536      */ 
    537     function remove_index($mailbox = null) 
    538     { 
    539         $this->db->query( 
    540             "DELETE FROM ".get_table_name('cache_index') 
    541             ." WHERE user_id = ".intval($this->userid) 
    542                 .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") 
    543         ); 
    544  
    545         if (strlen($mailbox)) 
     551     * @param bool    $remove      Enable to remove the DB row 
     552     */ 
     553    function remove_index($mailbox = null, $remove = false) 
     554    { 
     555        // The index should be only removed from database when 
     556        // UIDVALIDITY was detected or the mailbox is empty 
     557        // otherwise use 'valid' flag to not loose HIGHESTMODSEQ value 
     558        if ($remove) 
     559            $this->db->query( 
     560                "DELETE FROM ".get_table_name('cache_index') 
     561                ." WHERE user_id = ".intval($this->userid) 
     562                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") 
     563            ); 
     564        else 
     565            $this->db->query( 
     566                "UPDATE ".get_table_name('cache_index') 
     567                ." SET valid = 0" 
     568                ." WHERE user_id = ".intval($this->userid) 
     569                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") 
     570            ); 
     571 
     572        if (strlen($mailbox)) { 
    546573            unset($this->icache[$mailbox]['index']); 
     574            // Index removed, set flag to skip SELECT query in get_index() 
     575            $this->icache[$mailbox]['index_queried'] = true; 
     576        } 
    547577        else 
    548578            $this->icache = array(); 
     
    563593        ); 
    564594 
    565         if (strlen($mailbox)) 
     595        if (strlen($mailbox)) { 
    566596            unset($this->icache[$mailbox]['thread']); 
     597            // Thread data removed, set flag to skip SELECT query in get_thread() 
     598            $this->icache[$mailbox]['thread_queried'] = true; 
     599        } 
    567600        else 
    568601            $this->icache = array(); 
     
    578611    function clear($mailbox = null, $uids = null) 
    579612    { 
    580         $this->remove_index($mailbox); 
     613        $this->remove_index($mailbox, true); 
    581614        $this->remove_thread($mailbox); 
    582615        $this->remove_message($mailbox, $uids); 
     
    619652    } 
    620653 
    621  
    622654    /** 
    623655     * Fetches index data from database 
     
    627659        // Get index from DB 
    628660        $sql_result = $this->db->query( 
    629             "SELECT data" 
     661            "SELECT data, valid" 
    630662            ." FROM ".get_table_name('cache_index') 
    631663            ." WHERE user_id = ?" 
     
    637669 
    638670            return array( 
     671                'valid'      => $sql_arr['valid'], 
    639672                'seq'        => explode(',', $data[0]), 
    640673                'uid'        => explode(',', $data[1]), 
     
    644677                'validity'   => $data[5], 
    645678                'uidnext'    => $data[6], 
     679                'modseq'     => $data[7], 
    646680            ); 
    647681        } 
     
    667701            $data = explode('@', $sql_arr['data']); 
    668702 
     703            // Uncompress data, see add_thread_row() 
     704  //          $data[0] = str_replace(array('*', '^', '#'), array(';a:0:{}', 'i:', ';a:1:'), $data[0]); 
    669705            $data[0] = unserialize($data[0]); 
     706 
    670707            // build 'depth' and 'children' arrays 
    671708            $depth = $children = array(); 
     
    690727     */ 
    691728    private function add_index_row($mailbox, $sort_field, $sort_order, 
    692         $data = array(), $mbox_data = array(), $exists = false) 
     729        $data = array(), $mbox_data = array(), $exists = false, $modseq = null) 
    693730    { 
    694731        $data = array( 
     
    700737            (int) $mbox_data['UIDVALIDITY'], 
    701738            (int) $mbox_data['UIDNEXT'], 
     739            $modseq ? $modseq : $mbox_data['HIGHESTMODSEQ'], 
    702740        ); 
    703741        $data = implode('@', $data); 
     
    706744            $sql_result = $this->db->query( 
    707745                "UPDATE ".get_table_name('cache_index') 
    708                 ." SET data = ?, changed = ".$this->db->now() 
     746                ." SET data = ?, valid = 1, changed = ".$this->db->now() 
    709747                ." WHERE user_id = ?" 
    710748                    ." AND mailbox = ?", 
     
    713751            $sql_result = $this->db->query( 
    714752                "INSERT INTO ".get_table_name('cache_index') 
    715                 ." (user_id, mailbox, data, changed)" 
    716                 ." VALUES (?, ?, ?, ".$this->db->now().")", 
     753                ." (user_id, mailbox, data, valid, changed)" 
     754                ." VALUES (?, ?, ?, 1, ".$this->db->now().")", 
    717755                $this->userid, $mailbox, $data); 
    718756    } 
     
    724762    private function add_thread_row($mailbox, $data = array(), $mbox_data = array(), $exists = false) 
    725763    { 
     764        $tree = serialize($data['tree']); 
     765        // This significantly reduces data length 
     766//        $tree = str_replace(array(';a:0:{}', 'i:', ';a:1:'), array('*', '^', '#'), $tree); 
     767 
    726768        $data = array( 
    727             serialize($data['tree']), 
     769            $tree, 
    728770            (int) $this->skip_deleted, 
    729771            (int) $mbox_data['UIDVALIDITY'], 
     
    765807 
    766808        // Check UIDVALIDITY 
    767         // @TODO: while we're storing message sequence numbers in thread 
    768         //        index, should UIDVALIDITY invalidate the thread data? 
    769809        if ($index['validity'] != $mbox_data['UIDVALIDITY']) { 
    770             // the whole cache (all folders) is invalid 
    771             $this->clear(); 
     810            $this->clear($mailbox); 
    772811            $exists = false; 
    773812            return false; 
     
    781820        } 
    782821 
     822        // Validation flag 
     823        if (!$is_thread && empty($index['valid'])) { 
     824            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']); 
     825            return false; 
     826        } 
     827 
     828        // Index was created with different skip_deleted setting 
     829        if ($this->skip_deleted != $index['deleted']) { 
     830            return false; 
     831        } 
     832 
     833        // Check HIGHESTMODSEQ 
     834        if (!empty($index['modseq']) && !empty($mbox_data['HIGHESTMODSEQ']) 
     835            && $index['modseq'] == $mbox_data['HIGHESTMODSEQ'] 
     836        ) { 
     837            return true; 
     838        } 
     839 
    783840        // Check UIDNEXT 
    784841        if ($index['uidnext'] != $mbox_data['UIDNEXT']) { 
    785842            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']); 
    786             return false; 
    787         } 
    788  
    789         // Index was created with different skip_deleted setting 
    790         if ($this->skip_deleted != $index['deleted']) { 
    791843            return false; 
    792844        } 
     
    849901 
    850902    /** 
     903     * Synchronizes the mailbox. 
     904     * 
     905     * @param string $mailbox Folder name 
     906     */ 
     907    function synchronize($mailbox) 
     908    { 
     909        // RFC4549: Synchronization Operations for Disconnected IMAP4 Clients 
     910        // RFC4551: IMAP Extension for Conditional STORE Operation 
     911        //          or Quick Flag Changes Resynchronization 
     912        // RFC5162: IMAP Extensions for Quick Mailbox Resynchronization 
     913 
     914        // @TODO: synchronize with other methods? 
     915        $qresync   = $this->imap->get_capability('QRESYNC'); 
     916        $condstore = $qresync ? true : $this->imap->get_capability('CONDSTORE'); 
     917 
     918        if (!$qresync && !$condstore) { 
     919            return; 
     920        } 
     921 
     922        // Get stored index 
     923        $index = $this->get_index_row($mailbox); 
     924 
     925        // database is empty 
     926        if (empty($index)) { 
     927            // set the flag that DB was already queried for index 
     928            // this way we'll be able to skip one SELECT in get_index() 
     929            $this->icache[$mailbox]['index_queried'] = true; 
     930            return; 
     931        } 
     932 
     933        $this->icache[$mailbox]['index'] = $index; 
     934 
     935        // no last HIGHESTMODSEQ value 
     936        if (empty($index['modseq'])) { 
     937            return; 
     938        } 
     939 
     940        // NOTE: make sure the mailbox isn't selected, before 
     941        // enabling QRESYNC and invoking SELECT 
     942        if ($this->imap->conn->selected !== null) { 
     943            $this->imap->conn->close(); 
     944        } 
     945 
     946        // Enable QRESYNC 
     947        $res = $this->imap->conn->enable($qresync ? 'QRESYNC' : 'CONDSTORE'); 
     948        if (!is_array($res)) { 
     949            return; 
     950        } 
     951 
     952        // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.) 
     953        $mbox_data = $this->imap->mailbox_data($mailbox); 
     954 
     955        if (empty($mbox_data)) { 
     956             return; 
     957        } 
     958 
     959        // Check UIDVALIDITY 
     960        if ($index['validity'] != $mbox_data['UIDVALIDITY']) { 
     961            $this->clear($mailbox); 
     962            return; 
     963        } 
     964 
     965        // QRESYNC not supported on specified mailbox 
     966        if (!empty($mbox_data['NOMODSEQ']) || empty($mbox_data['HIGHESTMODSEQ'])) { 
     967            return; 
     968        } 
     969 
     970        // Nothing new 
     971        if ($mbox_data['HIGHESTMODSEQ'] == $index['modseq']) { 
     972            return; 
     973        } 
     974 
     975        // Get known uids 
     976        $uids = array(); 
     977        $sql_result = $this->db->query( 
     978            "SELECT uid" 
     979            ." FROM ".get_table_name('cache_messages') 
     980            ." WHERE user_id = ?" 
     981                ." AND mailbox = ?", 
     982            $this->userid, $mailbox); 
     983 
     984        while ($sql_arr = $this->db->fetch_assoc($sql_result)) { 
     985          $uids[] = $sql_arr['uid']; 
     986        } 
     987 
     988        // No messages in database, nothing to sync 
     989        if (empty($uids)) { 
     990            return; 
     991        } 
     992 
     993        // Get modified flags and vanished messages 
     994        // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED) 
     995        $result = $this->imap->conn->fetch($mailbox, 
     996            !empty($uids) ? $uids : '1:*', true, array('FLAGS'), 
     997            $index['modseq'], $qresync); 
     998 
     999        if (!empty($result)) { 
     1000            foreach ($result as $id => $msg) { 
     1001                $uid = $msg->uid; 
     1002                // Remove deleted message 
     1003                if ($this->skip_deleted && !empty($msg->flags['DELETED'])) { 
     1004                    $this->remove_message($mailbox, $uid); 
     1005                    continue; 
     1006                } 
     1007 
     1008                $flags = 0; 
     1009                if (!empty($msg->flags)) { 
     1010                    foreach ($this->flags as $idx => $flag) 
     1011                        if (!empty($msg->flags[$flag])) 
     1012                            $flags += $idx; 
     1013                } 
     1014 
     1015                $this->db->query( 
     1016                    "UPDATE ".get_table_name('cache_messages') 
     1017                    ." SET flags = ?, changed = ".$this->db->now() 
     1018                    ." WHERE user_id = ?" 
     1019                        ." AND mailbox = ?" 
     1020                        ." AND uid = ?" 
     1021                        ." AND flags <> ?", 
     1022                    $flags, $this->userid, $mailbox, $uid, $flags); 
     1023            } 
     1024        } 
     1025 
     1026        // Get VANISHED 
     1027        if ($qresync) { 
     1028            $mbox_data = $this->imap->mailbox_data($mailbox); 
     1029 
     1030            // Removed messages 
     1031            if (!empty($mbox_data['VANISHED'])) { 
     1032                $uids = rcube_imap_generic::uncompressMessageSet($mbox_data['VANISHED']); 
     1033                if (!empty($uids)) { 
     1034                    // remove messages from database 
     1035                    $this->remove_message($mailbox, $uids); 
     1036 
     1037                    // Invalidate thread indexes (?) 
     1038                    $this->remove_thread($mailbox); 
     1039                } 
     1040            } 
     1041        } 
     1042 
     1043        $sort_field = $index['sort_field']; 
     1044        $sort_order = $index['sort_order']; 
     1045        $exists     = true; 
     1046 
     1047        // Validate index 
     1048        if (!$this->validate($mailbox, $index, $exists)) { 
     1049            // Update index 
     1050            $data = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data); 
     1051        } 
     1052        else { 
     1053            $data = array_combine($index['seq'], $index['uid']); 
     1054        } 
     1055 
     1056        // update index and/or HIGHESTMODSEQ value 
     1057        $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, $exists); 
     1058 
     1059        // update internal cache for get_index() 
     1060        $this->icache[$mailbox]['index']['result'] = $data; 
     1061    } 
     1062 
     1063 
     1064    /** 
    8511065     * Converts cache row into message object. 
    8521066     * 
     
    8601074 
    8611075        if ($message) { 
    862             foreach ($this->flag_fields as $field) 
    863                 $message->$field = (bool) $sql_arr[$field]; 
     1076            $message->flags = array(); 
     1077            foreach ($this->flags as $idx => $flag) 
     1078                if (($sql_arr['flags'] & $idx) == $idx) 
     1079                    $message->flags[$flag] = true; 
    8641080        } 
    8651081 
     
    9071123     * Prepares message object to be stored in database. 
    9081124     */ 
    909     private function message_object_prepare($msg, $recursive = false) 
    910     { 
    911         // Remove body too big (>500kB) 
    912         if ($recursive || ($msg->body && strlen($msg->body) > 500 * 1024)) { 
     1125    private function message_object_prepare($msg) 
     1126    { 
     1127        // Remove body too big (>25kB) 
     1128        if ($msg->body && strlen($msg->body) > 25 * 1024) { 
    9131129            unset($msg->body); 
    9141130        } 
     
    9231139        if (is_array($msg->structure->parts)) { 
    9241140            foreach ($msg->structure->parts as $idx => $part) { 
    925                 $msg->structure->parts[$idx] = $this->message_object_prepare($part, true); 
     1141                $msg->structure->parts[$idx] = $this->message_object_prepare($part); 
    9261142            } 
    9271143        } 
     
    9291145        return $msg; 
    9301146    } 
     1147 
     1148 
     1149    /** 
     1150     * Fetches index data from IMAP server 
     1151     */ 
     1152    private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array()) 
     1153    { 
     1154        $data = array(); 
     1155 
     1156        if (empty($mbox_data)) { 
     1157            $mbox_data = $this->imap->mailbox_data($mailbox); 
     1158        } 
     1159 
     1160        // Prevent infinite loop. 
     1161        // It happens when rcube_imap::message_index_direct() is called. 
     1162        // There id2uid() is called which will again call get_index() and so on. 
     1163        if (!$sort_field && !$this->skip_deleted) 
     1164            $this->icache['pending_index_update'] = true; 
     1165 
     1166        if ($mbox_data['EXISTS']) { 
     1167            // fetch sorted sequence numbers 
     1168            $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); 
     1169            // fetch UIDs 
     1170            if (!empty($data_seq)) { 
     1171                // Seek in internal cache 
     1172                if (array_key_exists('index', (array)$this->icache[$mailbox]) 
     1173                    && array_key_exists('result', (array)$this->icache[$mailbox]['index']) 
     1174                ) 
     1175                    $data_uid = $this->icache[$mailbox]['index']['result']; 
     1176                else 
     1177                    $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq); 
     1178 
     1179                // build index 
     1180                if (!empty($data_uid)) { 
     1181                    foreach ($data_seq as $seq) 
     1182                        if ($uid = $data_uid[$seq]) 
     1183                            $data[$seq] = $uid; 
     1184                } 
     1185            } 
     1186        } 
     1187 
     1188        // Reset internal flags 
     1189        $this->icache['pending_index_update'] = false; 
     1190 
     1191        return $data; 
     1192    } 
    9311193} 
  • trunk/roundcubemail/program/include/rcube_imap_generic.php

    r5204 r5233  
    77 | This file is part of the Roundcube Webmail client                     | 
    88 | Copyright (C) 2005-2010, The Roundcube Dev Team                       | 
     9 | Copyright (C) 2011, Kolab Systems AG                                  | 
    910 | Licensed under the GNU GPL                                            | 
    1011 |                                                                       | 
     
    5556    public $priority; 
    5657    public $mdn_to; 
    57  
    58     public $flags; 
    59     public $mdnsent = false; 
    60     public $seen = false; 
    61     public $deleted = false; 
    62     public $answered = false; 
    63     public $forwarded = false; 
    64     public $flagged = false; 
    6558    public $others = array(); 
     59    public $flags = array(); 
    6660} 
    6761 
     
    690684        $this->error    = ''; 
    691685        $this->errornum = self::ERROR_OK; 
    692         $this->selected = ''; 
     686        $this->selected = null; 
    693687        $this->user     = $user; 
    694688        $this->host     = $host; 
     
    887881        } 
    888882 
    889         if ($this->selected == $mailbox) { 
     883        if ($this->selected === $mailbox) { 
    890884            return true; 
    891885        } 
     
    10501044 
    10511045        if ($result == self::ERROR_OK) { 
    1052             $this->selected = ''; // state has changed, need to reselect 
     1046            $this->selected = null; // state has changed, need to reselect 
    10531047            return true; 
    10541048        } 
     
    10681062 
    10691063        if ($result == self::ERROR_OK) { 
    1070             $this->selected = ''; 
     1064            $this->selected = null; 
    10711065            return true; 
    10721066        } 
     
    11351129 
    11361130        if ($res) { 
    1137             if ($this->selected == $mailbox) 
     1131            if ($this->selected === $mailbox) 
    11381132                $res = $this->close(); 
    11391133            else 
     
    11541148    { 
    11551149        if ($refresh) { 
    1156             $this->selected = ''; 
    1157         } 
    1158  
    1159         if ($this->selected == $mailbox) { 
     1150            $this->selected = null; 
     1151        } 
     1152 
     1153        if ($this->selected === $mailbox) { 
    11601154            return $this->data['EXISTS']; 
    11611155        } 
     
    11911185        $this->select($mailbox); 
    11921186 
    1193         if ($this->selected == $mailbox) { 
     1187        if ($this->selected === $mailbox) { 
    11941188            return $this->data['RECENT']; 
    11951189        } 
     
    16771671                        if (!empty($value)) { 
    16781672                            foreach ((array)$value as $flag) { 
    1679                                 $flag = str_replace('\\', '', $flag); 
    1680  
    1681                                 switch (strtoupper($flag)) { 
    1682                                 case 'SEEN': 
    1683                                     $result[$id]->seen = true; 
    1684                                     break; 
    1685                                 case 'DELETED': 
    1686                                     $result[$id]->deleted = true; 
    1687                                     break; 
    1688                                 case 'ANSWERED': 
    1689                                     $result[$id]->answered = true; 
    1690                                     break; 
    1691                                 case '$FORWARDED': 
    1692                                     $result[$id]->forwarded = true; 
    1693                                     break; 
    1694                                 case '$MDNSENT': 
    1695                                     $result[$id]->mdnsent = true; 
    1696                                     break; 
    1697                                 case 'FLAGGED': 
    1698                                     $result[$id]->flagged = true; 
    1699                                     break; 
    1700                                 default: 
    1701                                     $result[$id]->flags[] = $flag; 
    1702                                     break; 
    1703                                 } 
     1673                                $flag = str_replace(array('$', '\\'), '', $flag); 
     1674                                $flag = strtoupper($flag); 
     1675 
     1676                                $result[$id]->flags[$flag] = true; 
    17041677                            } 
    17051678                        } 
     
    18131786            // Sample: * VANISHED (EARLIER) 300:310,405,411 
    18141787 
    1815             else if (preg_match('/^\* VANISHED [EARLIER]*/i', $line, $match)) { 
     1788            else if (preg_match('/^\* VANISHED [()EARLIER]*/i', $line, $match)) { 
    18161789                $line   = substr($line, strlen($match[0])); 
    18171790                $v_data = $this->tokenizeResponse($line, 1); 
  • trunk/roundcubemail/program/js/app.js

    r5221 r5233  
    16481648    $.extend(this.env.messages[uid], { 
    16491649      deleted: flags.deleted?1:0, 
    1650       replied: flags.replied?1:0, 
    1651       unread: flags.unread?1:0, 
     1650      replied: flags.answered?1:0, 
     1651      unread: !flags.seen?1:0, 
    16521652      forwarded: flags.forwarded?1:0, 
    16531653      flagged: flags.flagged?1:0, 
     
    16721672      css_class = 'message' 
    16731673        + (even ? ' even' : ' odd') 
    1674         + (flags.unread ? ' unread' : '') 
     1674        + (!flags.seen ? ' unread' : '') 
    16751675        + (flags.deleted ? ' deleted' : '') 
    16761676        + (flags.flagged ? ' flagged' : '') 
    1677         + (flags.unread_children && !flags.unread && !this.env.autoexpand_threads ? ' unroot' : '') 
     1677        + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '') 
    16781678        + (message.selected ? ' selected' : ''), 
    16791679      // for performance use DOM instead of jQuery here 
     
    16901690      if (flags.deleted) 
    16911691        css_class += ' deleted'; 
    1692       else if (flags.unread) 
     1692      else if (!flags.seen) 
    16931693        css_class += ' unread'; 
    16941694      else if (flags.unread_children > 0) 
    16951695        css_class += ' unreadchildren'; 
    16961696    } 
    1697     if (flags.replied) 
     1697    if (flags.answered) 
    16981698      css_class += ' replied'; 
    16991699    if (flags.forwarded) 
     
    17631763        if (flags.deleted) 
    17641764          css_class = 'deleted'; 
    1765         else if (flags.unread) 
     1765        else if (!flags.seen) 
    17661766          css_class = 'unread'; 
    17671767        else if (flags.unread_children > 0) 
     
    20572057 
    20582058    while (new_row) { 
    2059       if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) 
    2060             && r.unread_children) { 
     2059      if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) && r.unread_children) { 
    20612060            this.message_list.expand_all(r); 
    20622061            this.set_unread_children(r.uid); 
     
    35433542  this.insert_recipient = function(id) 
    35443543  { 
    3545     if (!this.env.contacts[id] || !this.ksearch_input) 
     3544    if (id === null || !this.env.contacts[id] || !this.ksearch_input) 
    35463545      return; 
    35473546 
  • trunk/roundcubemail/program/steps/mail/check_recent.inc

    r5228 r5233  
    3535// check recent/unseen counts 
    3636foreach ($a_mailboxes as $mbox_name) { 
     37    $is_current = $mbox_name == $current; 
     38    if ($is_current) { 
     39        // Synchronize mailbox cache, handle flag changes 
     40        $IMAP->mailbox_sync($mbox_name); 
     41    } 
     42 
     43    // Get mailbox status 
    3744    $status = $IMAP->mailbox_status($mbox_name); 
    3845 
    3946    if ($status & 1) { 
    4047        // trigger plugin hook 
    41         $RCMAIL->plugins->exec_hook('new_messages', array('mailbox' => $mbox_name)); 
     48        $RCMAIL->plugins->exec_hook('new_messages', 
     49            array('mailbox' => $mbox_name, 'is_current' => $is_current)); 
    4250    } 
    4351 
    4452    rcmail_send_unread_count($mbox_name, true); 
    4553 
    46     if ($status && $mbox_name == $current) { 
     54    if ($status && $is_current) { 
    4755        // refresh saved search set 
    4856        $search_request = get_input_value('_search', RCUBE_INPUT_GPC); 
  • trunk/roundcubemail/program/steps/mail/compose.inc

    r5226 r5233  
    157157  $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT; 
    158158  $MESSAGE = new rcube_message($msg_uid); 
    159    
     159 
    160160  // make sure message is marked as read 
    161   if ($MESSAGE && $MESSAGE->headers && !$MESSAGE->headers->seen) 
     161  if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN'])) 
    162162    $IMAP->set_flag($msg_uid, 'SEEN'); 
    163163 
    164164  if (!empty($MESSAGE->headers->charset)) 
    165165    $IMAP->set_charset($MESSAGE->headers->charset); 
    166      
     166 
    167167  if ($compose_mode == RCUBE_COMPOSE_REPLY) 
    168168  { 
  • trunk/roundcubemail/program/steps/mail/func.inc

    r5226 r5233  
    288288    } 
    289289 
     290    $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags)); 
    290291    if ($header->depth) 
    291292      $a_msg_flags['depth'] = $header->depth; 
     
    298299    if ($header->unread_children) 
    299300      $a_msg_flags['unread_children'] = $header->unread_children; 
    300     if ($header->deleted) 
    301       $a_msg_flags['deleted'] = 1; 
    302     if (!$header->seen) 
    303       $a_msg_flags['unread'] = 1; 
    304     if ($header->answered) 
    305       $a_msg_flags['replied'] = 1; 
    306     if ($header->forwarded) 
    307       $a_msg_flags['forwarded'] = 1; 
    308     if ($header->flagged) 
    309       $a_msg_flags['flagged'] = 1; 
    310301    if ($header->others['list-post']) 
    311302      $a_msg_flags['ml'] = 1; 
     
    316307    $a_msg_flags['mbox'] = $mbox; 
    317308 
    318     // merge with plugin result 
     309    // merge with plugin result (Deprecated, use $header->flags) 
    319310    if (!empty($header->list_flags) && is_array($header->list_flags)) 
    320311      $a_msg_flags = array_merge($a_msg_flags, $header->list_flags); 
     
    14551446    $message = new rcube_message($message); 
    14561447 
    1457   if ($message->headers->mdn_to && !$message->headers->mdnsent && 
     1448  if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) && 
    14581449    ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*'))) 
    14591450  { 
  • trunk/roundcubemail/program/steps/mail/list.inc

    r4410 r5233  
    5353 
    5454$mbox_name = $IMAP->get_mailbox_name(); 
     55 
     56// Synchronize mailbox cache, handle flag changes 
     57$IMAP->mailbox_sync($mbox_name); 
    5558 
    5659// initialize searching result if search_filter is used 
     
    117120// send response 
    118121$OUTPUT->send(); 
    119  
    120  
  • trunk/roundcubemail/program/steps/mail/move_del.inc

    r4410 r5233  
    117117  } 
    118118 
    119   if ($RCMAIL->action=='moveto' && strlen($target)) { 
     119  if ($RCMAIL->action == 'moveto' && strlen($target)) { 
    120120    rcmail_send_unread_count($target, true); 
    121121  } 
  • trunk/roundcubemail/program/steps/mail/show.inc

    r5190 r5233  
    7777 
    7878  // check for unset disposition notification 
    79   if ($MESSAGE->headers->mdn_to && 
    80       !$MESSAGE->headers->mdnsent && !$MESSAGE->headers->seen && 
    81       ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')) && 
    82       $mbox_name != $CONFIG['drafts_mbox'] && 
    83       $mbox_name != $CONFIG['sent_mbox']) 
    84   { 
     79  if ($MESSAGE->headers->mdn_to 
     80      && empty($MESSAGE->headers->flags['MDNSENT']) 
     81      && empty($MESSAGE->headers->flags['SEEN']) 
     82      && ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')) 
     83      && $mbox_name != $CONFIG['drafts_mbox'] 
     84      && $mbox_name != $CONFIG['sent_mbox'] 
     85  ) { 
    8586    $mdn_cfg = intval($CONFIG['mdn_requests']); 
    8687 
     
    101102  } 
    102103 
    103   if (!$MESSAGE->headers->seen && ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0))) 
     104  if (empty($MESSAGE->headers->flags['SEEN']) 
     105    && ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0)) 
     106  ) { 
    104107    $RCMAIL->plugins->exec_hook('message_read', array('uid' => $MESSAGE->uid, 
    105108      'mailbox' => $mbox_name, 'message' => $MESSAGE)); 
     109  } 
    106110} 
    107111 
     
    200204 
    201205// mark message as read 
    202 if ($MESSAGE && $MESSAGE->headers && !$MESSAGE->headers->seen && 
     206if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']) && 
    203207  ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0))) 
    204208{ 
Note: See TracChangeset for help on using the changeset viewer.