Changeset 5217 in subversion
- Timestamp:
- Sep 15, 2011 5:42:52 AM (21 months ago)
- Location:
- branches/devel-mcache/roundcubemail
- Files:
-
- 9 edited
-
SQL/mssql.initial.sql (modified) (1 diff)
-
SQL/mysql.initial.sql (modified) (1 diff)
-
SQL/postgres.initial.sql (modified) (1 diff)
-
SQL/sqlite.initial.sql (modified) (1 diff)
-
program/include/rcube_imap.php (modified) (2 diffs)
-
program/include/rcube_imap_cache.php (modified) (25 diffs)
-
program/include/rcube_imap_generic.php (modified) (8 diffs)
-
program/steps/mail/check_recent.inc (modified) (1 diff)
-
program/steps/mail/list.inc (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/devel-mcache/roundcubemail/SQL/mssql.initial.sql
r5131 r5217 12 12 [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , 13 13 [changed] [datetime] NOT NULL , 14 [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , 14 15 [data] [text] COLLATE Latin1_General_CI_AI NOT NULL 15 16 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -
branches/devel-mcache/roundcubemail/SQL/mysql.initial.sql
r5131 r5217 56 56 `mailbox` varchar(255) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL, 57 57 `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00', 58 `valid` tinyint(1) NOT NULL DEFAULT '0', 58 59 `data` longtext NOT NULL, 59 60 CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`) -
branches/devel-mcache/roundcubemail/SQL/postgres.initial.sql
r5131 r5217 198 198 mailbox varchar(255) NOT NULL, 199 199 changed timestamp with time zone DEFAULT now() NOT NULL, 200 valid smallint NOT NULL DEFAULT 0, 200 201 data text NOT NULL, 201 202 PRIMARY KEY (user_id, mailbox) -
branches/devel-mcache/roundcubemail/SQL/sqlite.initial.sql
r5131 r5217 130 130 mailbox varchar(255) NOT NULL, 131 131 changed datetime NOT NULL default '0000-00-00 00:00:00', 132 valid smallint NOT NULL DEFAULT '0', 132 133 data text NOT NULL, 133 134 PRIMARY KEY (user_id, mailbox) -
branches/devel-mcache/roundcubemail/program/include/rcube_imap.php
r5193 r5217 3400 3400 if ($this->conn->select($mailbox)) 3401 3401 $this->mailbox = $mailbox; 3402 else 3403 return null; 3402 3404 } 3403 3405 … … 3485 3487 3486 3488 return $options; 3489 } 3490 3491 3492 /** 3493 * Synchronizes messages cache. 3494 * 3495 * @param string $mailbox Folder name 3496 */ 3497 public function mailbox_sync($mailbox) 3498 { 3499 if ($mcache = $this->get_mcache_engine()) { 3500 $mcache->synchronize($mailbox); 3501 } 3502 else { 3503 // @TODO: use QRESYNC/CONDSTORE to detect flag changes 3504 } 3487 3505 } 3488 3506 -
branches/devel-mcache/roundcubemail/program/include/rcube_imap_cache.php
r5193 r5217 106 106 107 107 // 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); 108 if (array_key_exists('index', $this->icache[$mailbox])) { 109 // The index was fetched from database already, but not validated yet 110 if (!array_key_exists('result', $this->icache[$mailbox]['index'])) { 111 $index = $this->icache[$mailbox]['index']; 112 } 113 // We've got a valid index 114 else if ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field 115 ) { 116 if ($this->icache[$mailbox]['index']['sort_order'] == $sort_order) 117 return $this->icache[$mailbox]['index']['result']; 118 else 119 return array_reverse($this->icache[$mailbox]['index']['result'], true); 120 } 115 121 } 116 122 117 123 // Get index from DB (if DB wasn't already queried) 118 if (empty($ this->icache[$mailbox]['index_queried'])) {124 if (empty($index) && empty($this->icache[$mailbox]['index_queried'])) { 119 125 $index = $this->get_index_row($mailbox); 120 126 … … 124 130 $this->icache[$mailbox]['index_queried'] = true; 125 131 } 126 $data = null; 132 133 $data = null; 127 134 128 135 // @TODO: Think about skipping validation checks. … … 132 139 // and many rcube_imap changes to connect when needed 133 140 134 // Entry exist , check cache status141 // Entry exists, check cache status 135 142 if (!empty($index)) { 136 143 $exists = true; … … 156 163 } 157 164 else { 165 if ($existing) { 166 return null; 167 } 168 else if ($sort_field == 'ANY') { 169 $sort_field = ''; 170 } 171 158 172 // Got it in internal cache, so the row already exist 159 173 $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 }167 174 } 168 175 … … 171 178 // Get mailbox data (UIDVALIDITY, counters, etc.) for status check 172 179 $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; 180 $data = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data); 203 181 204 182 // insert/update 205 $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, $exists); 183 $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, 184 $exists, $index['modseq']); 206 185 } 207 186 … … 210 189 'sort_field' => $sort_field, 211 190 'sort_order' => $sort_order, 191 'modseq' => !empty($index['modseq']) ? $index['modseq'] : $mbox_data['HIGHESTMODSEQ'] 212 192 ); 213 193 … … 353 333 * @param string $mailbox Folder name 354 334 * @param int $uid Message UID 335 * @param bool $update If message doesn't exists in cache it will be fetched 336 * from IMAP server 337 * @param bool $no_cache Enables internal cache usage 355 338 * 356 339 * @return rcube_mail_header Message data 357 340 */ 358 function get_message($mailbox, $uid )341 function get_message($mailbox, $uid, $update = true, $cache = true) 359 342 { 360 343 // Check internal cache … … 383 366 384 367 // Get the message from IMAP server 385 if (empty($message) ) {368 if (empty($message) && $update) { 386 369 $message = $this->imap->get_headers($uid, $mailbox, true); 387 370 // cache will be updated in close(), see below … … 394 377 // - set \Seen flag (UPDATE) 395 378 // This way we can skip one UPDATE 396 if (!empty($message) ) {379 if (!empty($message) && $cache) { 397 380 // Save current message from internal cache 398 381 $this->save_icache(); … … 534 517 * 535 518 * @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 ); 519 * @param bool $remove Enable to remove the DB row 520 */ 521 function remove_index($mailbox = null, $remove = false) 522 { 523 // The index should be only removed from database when 524 // UIDVALIDITY was detected or the mailbox is empty 525 // otherwise use 'valid' flag to not loose HIGHESTMODSEQ value 526 if ($remove) 527 $this->db->query( 528 "DELETE FROM ".get_table_name('cache_index') 529 ." WHERE user_id = ".intval($this->userid) 530 .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") 531 ); 532 else 533 $this->db->query( 534 "UPDATE ".get_table_name('cache_index') 535 ." SET valid = 0" 536 ." WHERE user_id = ".intval($this->userid) 537 .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") 538 ); 544 539 545 540 if (strlen($mailbox)) … … 578 573 function clear($mailbox = null, $uids = null) 579 574 { 580 $this->remove_index($mailbox );575 $this->remove_index($mailbox, true); 581 576 $this->remove_thread($mailbox); 582 577 $this->remove_message($mailbox, $uids); … … 619 614 } 620 615 621 622 616 /** 623 617 * Fetches index data from database … … 627 621 // Get index from DB 628 622 $sql_result = $this->db->query( 629 "SELECT data "623 "SELECT data, valid" 630 624 ." FROM ".get_table_name('cache_index') 631 625 ." WHERE user_id = ?" … … 637 631 638 632 return array( 633 'valid' => $sql_arr['valid'], 639 634 'seq' => explode(',', $data[0]), 640 635 'uid' => explode(',', $data[1]), … … 644 639 'validity' => $data[5], 645 640 'uidnext' => $data[6], 641 'modseq' => $data[7], 646 642 ); 647 643 } … … 690 686 */ 691 687 private function add_index_row($mailbox, $sort_field, $sort_order, 692 $data = array(), $mbox_data = array(), $exists = false )688 $data = array(), $mbox_data = array(), $exists = false, $modseq = null) 693 689 { 694 690 $data = array( … … 700 696 (int) $mbox_data['UIDVALIDITY'], 701 697 (int) $mbox_data['UIDNEXT'], 698 $modseq ? $modseq : $mbox_data['HIGHESTMODSEQ'], 702 699 ); 703 700 $data = implode('@', $data); … … 706 703 $sql_result = $this->db->query( 707 704 "UPDATE ".get_table_name('cache_index') 708 ." SET data = ?, changed = ".$this->db->now()705 ." SET data = ?, valid = 1, changed = ".$this->db->now() 709 706 ." WHERE user_id = ?" 710 707 ." AND mailbox = ?", … … 713 710 $sql_result = $this->db->query( 714 711 "INSERT INTO ".get_table_name('cache_index') 715 ." (user_id, mailbox, data, changed)"716 ." VALUES (?, ?, ?, ".$this->db->now().")",712 ." (user_id, mailbox, data, valid, changed)" 713 ." VALUES (?, ?, ?, 1, ".$this->db->now().")", 717 714 $this->userid, $mailbox, $data); 718 715 } … … 765 762 766 763 // Check UIDVALIDITY 767 // @TODO: while we're storing message sequence numbers in thread768 // index, should UIDVALIDITY invalidate the thread data?769 764 if ($index['validity'] != $mbox_data['UIDVALIDITY']) { 770 // the whole cache (all folders) is invalid 771 $this->clear(); 765 $this->clear($mailbox); 772 766 $exists = false; 773 767 return false; … … 781 775 } 782 776 777 // Validation flag 778 if (!$is_thread && empty($index['valid'])) { 779 unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']); 780 return false; 781 } 782 783 // Index was created with different skip_deleted setting 784 if ($this->skip_deleted != $index['deleted']) { 785 return false; 786 } 787 788 // Check HIGHESTMODSEQ 789 if (!empty($index['modseq']) && !empty($mbox_data['HIGHESTMODSEQ']) 790 && $index['modseq'] == $mbox_data['HIGHESTMODSEQ'] 791 ) { 792 return true; 793 } 794 783 795 // Check UIDNEXT 784 796 if ($index['uidnext'] != $mbox_data['UIDNEXT']) { 785 797 unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']); 786 return false;787 }788 789 // Index was created with different skip_deleted setting790 if ($this->skip_deleted != $index['deleted']) {791 798 return false; 792 799 } … … 849 856 850 857 /** 858 * Synchronizes the mailbox. 859 * 860 * @param string $mailbox Folder name 861 */ 862 function synchronize($mailbox) 863 { 864 // RFC4549: Synchronization Operations for Disconnected IMAP4 Clients 865 // RFC4551: IMAP Extension for Conditional STORE Operation 866 // or Quick Flag Changes Resynchronization 867 // RFC5162: IMAP Extensions for Quick Mailbox Resynchronization 868 869 // @TODO: synchronize with other methods? 870 $qresync = $this->imap->get_capability('QRESYNC'); 871 $condstore = $qresync ? true : $this->imap->get_capability('CONDSTORE'); 872 873 if (!$qresync && !$condstore) { 874 return; 875 } 876 877 // Get stored index 878 $index = $this->get_index_row($mailbox); 879 880 // database is empty 881 if (empty($index)) { 882 // set the flag that DB was already queried for index 883 // this way we'll be able to skip one SELECT in get_index() 884 $this->icache[$mailbox]['index_queried'] = true; 885 return; 886 } 887 888 $this->icache[$mailbox]['index'] = $index; 889 890 // no last HIGHESTMODSEQ value 891 if (empty($index['modseq'])) { 892 return; 893 } 894 895 // NOTE: make sure the mailbox isn't selected, before 896 // enabling QRESYNC and invoking SELECT 897 if ($this->imap->conn->selected !== null) { 898 $this->imap->conn->close(); 899 } 900 901 // Enable QRESYNC 902 $res = $this->imap->conn->enable($qresync ? 'QRESYNC' : 'CONDSTORE'); 903 if (!is_array($res)) { 904 return; 905 } 906 907 // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.) 908 $mbox_data = $this->imap->mailbox_data($mailbox); 909 910 if (empty($mbox_data)) { 911 return; 912 } 913 914 // Check UIDVALIDITY 915 if ($index['validity'] != $mbox_data['UIDVALIDITY']) { 916 $this->clear($mailbox); 917 return; 918 } 919 920 // QRESYNC not supported on specified mailbox 921 if (!empty($mbox_data['NOMODSEQ']) || empty($mbox_data['HIGHESTMODSEQ'])) { 922 return; 923 } 924 925 // Nothing new 926 if ($mbox_data['HIGHESTMODSEQ'] == $index['modseq']) { 927 return; 928 } 929 930 // Get known uids 931 $uids = array(); 932 $sql_result = $this->db->query( 933 "SELECT uid" 934 ." FROM ".get_table_name('cache_messages') 935 ." WHERE user_id = ?" 936 ." AND mailbox = ?", 937 $this->userid, $mailbox); 938 939 while ($sql_arr = $this->db->fetch_assoc($sql_result)) { 940 $uids[] = $sql_arr['uid']; 941 } 942 943 // No messages in database, nothing to sync 944 if (empty($uids)) { 945 return; 946 } 947 948 $flags = $this->flag_fields; 949 950 // Get modified flags and vanished messages 951 // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED) 952 $result = $this->imap->conn->fetch($mailbox, 953 !empty($uids) ? $uids : '1:*', true, array('FLAGS'), 954 $index['modseq'], $qresync); 955 956 if (!empty($result)) { 957 foreach ($result as $id => $msg) { 958 $uid = $msg->uid; 959 // Remove deleted message 960 if ($this->skip_deleted && $msg->deleted) { 961 $this->remove_message($mailbox, $uid); 962 continue; 963 } 964 965 // Get the message to merge the flags (if it exists) 966 // @TODO: update flags without fetching the cached message 967 $message = $this->get_message($mailbox, $uid, false, false); 968 969 if (empty($message)) 970 continue; 971 972 $message->id = $id; 973 974 // reset message flags 975 $message->flags = $msg->flags; 976 foreach ($flags as $flag) 977 $message->$flag = $msg->$flag; 978 979 $this->add_message($mailbox, $message); 980 } 981 } 982 983 // Get VANISHED 984 if ($qresync) { 985 $mbox_data = $this->imap->mailbox_data($mailbox); 986 987 // Removed messages 988 if (!empty($mbox_data['VANISHED'])) { 989 $uids = rcube_imap_generic::uncompressMessageSet($mbox_data['VANISHED']); 990 if (!empty($uids)) { 991 // remove messages from database 992 $this->remove_message($mailbox, $uids); 993 994 // Invalidate thread indexes (?) 995 $this->remove_thread($mailbox); 996 } 997 } 998 } 999 1000 $sort_field = $index['sort_field']; 1001 $sort_order = $index['sort_order']; 1002 $exists = true; 1003 1004 // Validate index 1005 if (!$this->validate($mailbox, $index, $exists)) { 1006 // Update index 1007 $data = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data); 1008 } 1009 else { 1010 $data = array_combine($index['seq'], $index['uid']); 1011 } 1012 1013 // update index and/or HIGHESTMODSEQ value 1014 $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, $exists); 1015 1016 // update internal cache for get_index() 1017 $this->icache[$mailbox]['index']['result'] = $data; 1018 } 1019 1020 1021 /** 851 1022 * Converts cache row into message object. 852 1023 * … … 907 1078 * Prepares message object to be stored in database. 908 1079 */ 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)) {1080 private function message_object_prepare($msg) 1081 { 1082 // Remove body too big (>25kB) 1083 if ($msg->body && strlen($msg->body) > 25 * 1024) { 913 1084 unset($msg->body); 914 1085 } … … 923 1094 if (is_array($msg->structure->parts)) { 924 1095 foreach ($msg->structure->parts as $idx => $part) { 925 $msg->structure->parts[$idx] = $this->message_object_prepare($part , true);1096 $msg->structure->parts[$idx] = $this->message_object_prepare($part); 926 1097 } 927 1098 } … … 929 1100 return $msg; 930 1101 } 1102 1103 1104 /** 1105 * Fetches index data from IMAP server 1106 */ 1107 private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array()) 1108 { 1109 $data = array(); 1110 1111 if (empty($mbox_data)) { 1112 $mbox_data = $this->imap->mailbox_data($mailbox); 1113 } 1114 1115 // Prevent infinite loop. 1116 // It happens when rcube_imap::message_index_direct() is called. 1117 // There id2uid() is called which will again call get_index() and so on. 1118 if (!$sort_field && !$this->skip_deleted) 1119 $this->icache['pending_index_update'] = true; 1120 1121 if ($mbox_data['EXISTS']) { 1122 // fetch sorted sequence numbers 1123 $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); 1124 // fetch UIDs 1125 if (!empty($data_seq)) { 1126 // Seek in internal cache 1127 if (array_key_exists('index', (array)$this->icache[$mailbox]) 1128 && array_key_exists('result', (array)$this->icache[$mailbox]['index']) 1129 ) 1130 $data_uid = $this->icache[$mailbox]['index']['result']; 1131 else 1132 $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq); 1133 1134 // build index 1135 if (!empty($data_uid)) { 1136 foreach ($data_seq as $seq) 1137 if ($uid = $data_uid[$seq]) 1138 $data[$seq] = $uid; 1139 } 1140 } 1141 } 1142 1143 // Reset internal flags 1144 $this->icache['pending_index_update'] = false; 1145 1146 return $data; 1147 } 931 1148 } -
branches/devel-mcache/roundcubemail/program/include/rcube_imap_generic.php
r5114 r5217 689 689 $this->error = ''; 690 690 $this->errornum = self::ERROR_OK; 691 $this->selected = '';691 $this->selected = null; 692 692 $this->user = $user; 693 693 $this->host = $host; … … 881 881 } 882 882 883 if ($this->selected == $mailbox) {883 if ($this->selected === $mailbox) { 884 884 return true; 885 885 } … … 1044 1044 1045 1045 if ($result == self::ERROR_OK) { 1046 $this->selected = ''; // state has changed, need to reselect1046 $this->selected = null; // state has changed, need to reselect 1047 1047 return true; 1048 1048 } … … 1062 1062 1063 1063 if ($result == self::ERROR_OK) { 1064 $this->selected = '';1064 $this->selected = null; 1065 1065 return true; 1066 1066 } … … 1129 1129 1130 1130 if ($res) { 1131 if ($this->selected == $mailbox)1131 if ($this->selected === $mailbox) 1132 1132 $res = $this->close(); 1133 1133 else … … 1148 1148 { 1149 1149 if ($refresh) { 1150 $this->selected = '';1151 } 1152 1153 if ($this->selected == $mailbox) {1150 $this->selected = null; 1151 } 1152 1153 if ($this->selected === $mailbox) { 1154 1154 return $this->data['EXISTS']; 1155 1155 } … … 1185 1185 $this->select($mailbox); 1186 1186 1187 if ($this->selected == $mailbox) {1187 if ($this->selected === $mailbox) { 1188 1188 return $this->data['RECENT']; 1189 1189 } … … 1807 1807 // Sample: * VANISHED (EARLIER) 300:310,405,411 1808 1808 1809 else if (preg_match('/^\* VANISHED [ EARLIER]*/i', $line, $match)) {1809 else if (preg_match('/^\* VANISHED [()EARLIER]*/i', $line, $match)) { 1810 1810 $line = substr($line, strlen($match[0])); 1811 1811 $v_data = $this->tokenizeResponse($line, 1); -
branches/devel-mcache/roundcubemail/program/steps/mail/check_recent.inc
r4872 r5217 35 35 // check recent/unseen counts 36 36 foreach ($a_mailboxes as $mbox_name) { 37 if ($mbox_name == $current && ($status = $IMAP->mailbox_status($mbox_name))) { 37 if ($mbox_name == $current) { 38 // Synchronize mailbox cache, handle flag changes 39 $IMAP->mailbox_sync($mbox_name); 38 40 41 // Get mailbox status 42 $status = $IMAP->mailbox_status($mbox_name); 43 } 44 else { 45 $status = null; 46 } 47 48 if ($status) { 39 49 rcmail_send_unread_count($mbox_name, true); 40 50 -
branches/devel-mcache/roundcubemail/program/steps/mail/list.inc
r4410 r5217 53 53 54 54 $mbox_name = $IMAP->get_mailbox_name(); 55 56 // Synchronize mailbox cache, handle flag changes 57 $IMAP->mailbox_sync($mbox_name); 55 58 56 59 // initialize searching result if search_filter is used … … 117 120 // send response 118 121 $OUTPUT->send(); 119 120
Note: See TracChangeset
for help on using the changeset viewer.
