Changeset 5557 in subversion
- Timestamp:
- Dec 7, 2011 3:44:48 AM (18 months ago)
- Location:
- trunk/roundcubemail
- Files:
-
- 2 added
- 8 edited
-
CHANGELOG (modified) (1 diff)
-
program/include/rcube_imap.php (modified) (41 diffs)
-
program/include/rcube_imap_cache.php (modified) (29 diffs)
-
program/include/rcube_imap_generic.php (modified) (14 diffs)
-
program/include/rcube_result_index.php (added)
-
program/include/rcube_result_thread.php (added)
-
program/steps/mail/func.inc (modified) (1 diff)
-
program/steps/mail/pagenav.inc (modified) (2 diffs)
-
program/steps/mail/search.inc (modified) (2 diffs)
-
program/steps/mail/sendmail.inc (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/roundcubemail/CHANGELOG
r5544 r5557 2 2 =========================== 3 3 4 - Fix issues with big memory allocation of IMAP results 4 5 - Replace prompt() with jQuery UI dialog (#1485135) 5 6 - Fix navigation in messages search results -
trunk/roundcubemail/program/include/rcube_imap.php
r5525 r5557 432 432 * 433 433 * @param string IMAP Search query 434 * @param array List of message ids or NULL if empty434 * @param rcube_result_index|rcube_result_thread Result set 435 435 * @param string Charset of search string 436 436 * @param string Sorting field 437 437 * @param string True if set is sorted (SORT was used for searching) 438 438 */ 439 function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null, $threads=false, $sorted=false) 440 { 441 if (is_array($str) && $msgs == null) 442 list($str, $msgs, $charset, $sort_field, $threads, $sorted) = $str; 443 if ($msgs === false) 444 $msgs = array(); 445 else if ($msgs != null && !is_array($msgs)) 446 $msgs = explode(',', $msgs); 439 function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null, $sorted=false) 440 { 441 if (is_array($str) && $msgs === null) 442 list($str, $msgs, $charset, $sort_field, $sorted) = $str; 447 443 448 444 $this->search_string = $str; … … 450 446 $this->search_charset = $charset; 451 447 $this->search_sort_field = $sort_field; 452 $this->search_threads = $threads;453 448 $this->search_sorted = $sorted; 449 $this->search_threads = is_a($this->search_set, 'rcube_result_thread'); 454 450 } 455 451 … … 457 453 /** 458 454 * Return the saved search set as hash array 455 * 456 * @param bool $clone Clone result object 457 * 459 458 * @return array Search set 460 459 */ 461 460 function get_search_set() 462 461 { 463 return array($this->search_string, 462 return array( 463 $this->search_string, 464 464 $this->search_set, 465 465 $this->search_charset, 466 466 $this->search_sort_field, 467 $this->search_threads,468 467 $this->search_sorted, 469 468 ); … … 690 689 // count search set 691 690 if ($this->search_string && $mailbox == $this->mailbox && ($mode == 'ALL' || $mode == 'THREADS') && !$force) { 692 if ($ this->search_threads)693 return $ mode == 'ALL' ? count((array)$this->search_set['depth']) : count((array)$this->search_set['tree']);691 if ($mode == 'ALL') 692 return $this->search_set->countMessages(); 694 693 else 695 return count((array)$this->search_set);694 return $this->search_set->count(); 696 695 } 697 696 … … 706 705 707 706 if ($mode == 'THREADS') { 708 $res = $this-> _threadcount($mailbox, $msg_count);709 $count = $res ['count'];707 $res = $this->fetch_threads($mailbox, $force); 708 $count = $res->count(); 710 709 711 710 if ($status) { 712 $this->set_folder_stats($mailbox, 'cnt', $res['msgcount']); 713 $this->set_folder_stats($mailbox, 'maxuid', $res['maxuid'] ? $this->id2uid($res['maxuid'], $mailbox) : 0); 711 $msg_count = $res->countMessages(); 712 $this->set_folder_stats($mailbox, 'cnt', $msg_count); 713 $this->set_folder_stats($mailbox, 'maxuid', $msg_count ? $this->id2uid($msg_count, $mailbox) : 0); 714 714 } 715 715 } … … 722 722 $search_str = "ALL UNDELETED"; 723 723 $keys = array('COUNT'); 724 $need_uid = false;725 724 726 725 if ($mode == 'UNSEEN') { … … 733 732 if ($status) { 734 733 $keys[] = 'MAX'; 735 $need_uid = true;736 734 } 737 735 } 736 737 // @TODO: if $force==false && $mode == 'ALL' we could try to use cache index here 738 738 739 739 // get message count using (E)SEARCH 740 740 // not very performant but more precise (using UNDELETED) 741 $index = $this->conn->search($mailbox, $search_str, $need_uid, $keys); 742 743 $count = is_array($index) ? $index['COUNT'] : 0; 741 $index = $this->conn->search($mailbox, $search_str, true, $keys); 742 $count = $index->count(); 744 743 745 744 if ($mode == 'ALL') { 746 if ($this->messages_caching) { 747 // Save additional info required by cache status check 748 $this->icache['undeleted_idx'] = array($mailbox, $index['ALL'], $index['COUNT']); 749 } 745 // Cache index data, will be used in message_index_direct() 746 $this->icache['undeleted_idx'] = $index; 747 750 748 if ($status) { 751 749 $this->set_folder_stats($mailbox, 'cnt', $count); 752 $this->set_folder_stats($mailbox, 'maxuid', is_array($index) ? $index['MAX'] : 0);750 $this->set_folder_stats($mailbox, 'maxuid', $index->max()); 753 751 } 754 752 } … … 776 774 777 775 /** 778 * Private method for getting nr of threads779 *780 * @param string $mailbox Folder name781 *782 * @returns array Array containing items: 'count' - threads count,783 * 'msgcount' = messages count, 'maxuid' = max. UID in the set784 * @access private785 */786 private function _threadcount($mailbox)787 {788 $result = array();789 790 if (!empty($this->icache['threads'])) {791 $dcount = count($this->icache['threads']['depth']);792 $result = array(793 'count' => count($this->icache['threads']['tree']),794 'msgcount' => $dcount,795 'maxuid' => $dcount ? max(array_keys($this->icache['threads']['depth'])) : 0,796 );797 }798 else if (is_array($result = $this->fetch_threads($mailbox))) {799 $dcount = count($result[1]);800 $result = array(801 'count' => count($result[0]),802 'msgcount' => $dcount,803 'maxuid' => $dcount ? max(array_keys($result[1])) : 0,804 );805 }806 807 return $result;808 }809 810 811 /**812 776 * Public method for listing headers 813 777 * convert mailbox name with root dir first … … 818 782 * @param string $sort_order Sort order [ASC|DESC] 819 783 * @param int $slice Number of slice items to extract from result array 784 * 820 785 * @return array Indexed array with message header objects 821 786 * @access public 822 787 */ 823 function list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)788 public function list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) 824 789 { 825 790 if (!strlen($mailbox)) { … … 845 810 private function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) 846 811 { 847 if (!strlen($mailbox)) 812 if (!strlen($mailbox)) { 848 813 return array(); 814 } 815 816 $this->set_sort_order($sort_field, $sort_order); 817 $page = $page ? $page : $this->list_page; 849 818 850 819 // use saved message set 851 if ($this->search_string && $mailbox == $this->mailbox) 852 return $this->_list_header_set($mailbox, $page, $sort_field, $sort_order, $slice); 853 854 if ($this->threading) 855 return $this->_list_thread_headers($mailbox, $page, $sort_field, $sort_order, $slice); 856 857 $this->_set_sort_order($sort_field, $sort_order); 858 859 $page = $page ? $page : $this->list_page; 860 861 // Use messages cache 862 if ($mcache = $this->get_mcache_engine()) { 863 $msg_index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order); 864 865 if (empty($msg_index)) 866 return array(); 867 868 $from = ($page-1) * $this->page_size; 869 $to = $from + $this->page_size; 870 $msg_index = array_values($msg_index); // UIDs 871 $is_uid = true; 872 $sorted = true; 873 874 if ($from || $to) 875 $msg_index = array_slice($msg_index, $from, $to - $from); 876 877 if ($slice) 878 $msg_index = array_slice($msg_index, -$slice, $slice); 879 880 $a_msg_headers = $mcache->get_messages($mailbox, $msg_index); 881 } 882 // retrieve headers from IMAP 883 // use message index sort as default sorting (for better performance) 884 else if (!$this->sort_field) { 885 if ($this->skip_deleted) { 886 // @TODO: this could be cached 887 if ($msg_index = $this->_search_index($mailbox, 'ALL UNDELETED')) { 888 list($begin, $end) = $this->_get_message_range(count($msg_index), $page); 889 $msg_index = array_slice($msg_index, $begin, $end-$begin); 890 } 891 } 892 else if ($max = $this->conn->countMessages($mailbox)) { 893 list($begin, $end) = $this->_get_message_range($max, $page); 894 $msg_index = range($begin+1, $end); 895 } 896 else 897 $msg_index = array(); 898 899 if ($slice && $msg_index) 900 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); 901 902 // fetch reqested headers from server 903 if ($msg_index) 904 $a_msg_headers = $this->fetch_headers($mailbox, $msg_index); 905 } 906 // use SORT command 907 else if ($this->get_capability('SORT') && 908 // Courier-IMAP provides SORT capability but allows to disable it by admin (#1486959) 909 ($msg_index = $this->conn->sort($mailbox, $this->sort_field, 910 $this->skip_deleted ? 'UNDELETED' : '', true)) !== false 911 ) { 912 if (!empty($msg_index)) { 913 list($begin, $end) = $this->_get_message_range(count($msg_index), $page); 914 $msg_index = array_slice($msg_index, $begin, $end-$begin); 915 $is_uid = true; 916 917 if ($slice) 918 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); 919 920 // fetch reqested headers from server 921 $a_msg_headers = $this->fetch_headers($mailbox, $msg_index, true); 922 } 923 } 924 // fetch specified header for all messages and sort 925 else if ($msg_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 926 $this->sort_field, $this->skip_deleted) 927 ) { 928 asort($msg_index); // ASC 929 $msg_index = array_keys($msg_index); 930 list($begin, $end) = $this->_get_message_range(count($msg_index), $page); 931 $msg_index = array_slice($msg_index, $begin, $end-$begin); 932 933 if ($slice) 934 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); 935 936 // fetch reqested headers from server 937 $a_msg_headers = $this->fetch_headers($mailbox, $msg_index); 938 } 939 940 // return empty array if no messages found 941 if (!is_array($a_msg_headers) || empty($a_msg_headers)) 820 if ($this->search_string && $mailbox == $this->mailbox) { 821 return $this->_list_header_set($mailbox, $page, $slice); 822 } 823 824 if ($this->threading) { 825 return $this->_list_thread_headers($mailbox, $page, $slice); 826 } 827 828 // get UIDs of all messages in the folder, sorted 829 $index = $this->message_index($mailbox, $this->sort_field, $this->sort_order); 830 831 if ($index->isEmpty()) { 942 832 return array(); 943 944 // use this class for message sorting 945 $sorter = new rcube_header_sorter(); 946 $sorter->set_index($msg_index, $is_uid); 947 $sorter->sort_headers($a_msg_headers); 948 949 if ($this->sort_order == 'DESC' && !$sorted) 950 $a_msg_headers = array_reverse($a_msg_headers); 833 } 834 835 $from = ($page-1) * $this->page_size; 836 $to = $from + $this->page_size; 837 838 $index->slice($from, $to - $from); 839 840 if ($slice) 841 $index->slice(-$slice, $slice); 842 843 // fetch reqested messages headers 844 $a_index = $index->get(); 845 $a_msg_headers = $this->fetch_headers($mailbox, $a_index); 951 846 952 847 return array_values($a_msg_headers); … … 959 854 * @param string $mailbox Mailbox/folder name 960 855 * @param int $page Current page to list 961 * @param string $sort_field Header field to sort by962 * @param string $sort_order Sort order [ASC|DESC]963 856 * @param int $slice Number of slice items to extract from result array 964 857 * … … 966 859 * @see rcube_imap::list_headers 967 860 */ 968 private function _list_thread_headers($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) 969 { 970 $this->_set_sort_order($sort_field, $sort_order); 971 972 $page = $page ? $page : $this->list_page; 973 $mcache = $this->get_mcache_engine(); 974 861 private function _list_thread_headers($mailbox, $page, $slice=0) 862 { 975 863 // get all threads (not sorted) 976 if ($mcache )977 list ($thread_tree, $msg_depth, $has_children)= $mcache->get_thread($mailbox);864 if ($mcache = $this->get_mcache_engine()) 865 $threads = $mcache->get_thread($mailbox); 978 866 else 979 list ($thread_tree, $msg_depth, $has_children) = $this->fetch_threads($mailbox); 980 981 if (empty($thread_tree)) 982 return array(); 983 984 $msg_index = $this->sort_threads($mailbox, $thread_tree); 985 986 return $this->_fetch_thread_headers($mailbox, 987 $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice); 867 $threads = $this->fetch_threads($mailbox); 868 869 return $this->_fetch_thread_headers($mailbox, $threads, $page, $slice); 988 870 } 989 871 … … 995 877 * @param bool $force Use IMAP server, no cache 996 878 * 997 * @return array Array with thread data879 * @return rcube_imap_thread Thread data object 998 880 */ 999 881 function fetch_threads($mailbox, $force = false) … … 1007 889 // get all threads 1008 890 $result = $this->conn->thread($mailbox, $this->threading, 1009 $this->skip_deleted ? 'UNDELETED' : '' );891 $this->skip_deleted ? 'UNDELETED' : '', true); 1010 892 1011 893 // add to internal (fast) cache 1012 $this->icache['threads'] = array(); 1013 $this->icache['threads']['tree'] = is_array($result) ? $result[0] : array(); 1014 $this->icache['threads']['depth'] = is_array($result) ? $result[1] : array(); 1015 $this->icache['threads']['has_children'] = is_array($result) ? $result[2] : array(); 1016 } 1017 1018 return array( 1019 $this->icache['threads']['tree'], 1020 $this->icache['threads']['depth'], 1021 $this->icache['threads']['has_children'], 1022 ); 894 $this->icache['threads'] = $result; 895 } 896 897 return $this->icache['threads']; 1023 898 } 1024 899 … … 1027 902 * Private method for fetching threaded messages headers 1028 903 * 1029 * @param string $mailbox Mailbox name 1030 * @param array $thread_tree Thread tree data 1031 * @param array $msg_depth Thread depth data 1032 * @param array $has_children Thread children data 1033 * @param array $msg_index Messages index 1034 * @param int $page List page number 1035 * @param int $slice Number of threads to slice 904 * @param string $mailbox Mailbox name 905 * @param rcube_result_thread $threads Threads data object 906 * @param int $page List page number 907 * @param int $slice Number of threads to slice 1036 908 * 1037 909 * @return array Messages headers 1038 910 * @access private 1039 911 */ 1040 private function _fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0) 1041 { 1042 // now get IDs for current page 1043 list($begin, $end) = $this->_get_message_range(count($msg_index), $page); 1044 $msg_index = array_slice($msg_index, $begin, $end-$begin); 912 private function _fetch_thread_headers($mailbox, $threads, $page, $slice=0) 913 { 914 // Sort thread structure 915 $this->sort_threads($threads); 916 917 $from = ($page-1) * $this->page_size; 918 $to = $from + $this->page_size; 919 920 $threads->slice($from, $to - $from); 1045 921 1046 922 if ($slice) 1047 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); 1048 1049 if ($this->sort_order == 'DESC') 1050 $msg_index = array_reverse($msg_index); 1051 1052 // flatten threads array 1053 // @TODO: fetch children only in expanded mode (?) 1054 $all_ids = array(); 1055 foreach ($msg_index as $root) { 1056 $all_ids[] = $root; 1057 if (!empty($thread_tree[$root])) 1058 $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); 1059 } 923 $threads->slice(-$slice, $slice); 924 925 // Get UIDs of all messages in all threads 926 $a_index = $threads->get(); 1060 927 1061 928 // fetch reqested headers from server 1062 $a_msg_headers = $this->fetch_headers($mailbox, $all_ids); 1063 1064 // return empty array if no messages found 1065 if (!is_array($a_msg_headers) || empty($a_msg_headers)) 1066 return array(); 1067 1068 // use this class for message sorting 1069 $sorter = new rcube_header_sorter(); 1070 $sorter->set_index($all_ids); 1071 $sorter->sort_headers($a_msg_headers); 929 $a_msg_headers = $this->fetch_headers($mailbox, $a_index); 930 931 unset($a_index); 1072 932 1073 933 // Set depth, has_children and unread_children fields in headers 1074 $this->_set_thread_flags($a_msg_headers, $ msg_depth, $has_children);934 $this->_set_thread_flags($a_msg_headers, $threads); 1075 935 1076 936 return array_values($a_msg_headers); … … 1082 942 * depth, has_children and unread_children 1083 943 * 1084 * @param array $headers Reference to headers array indexed by messageID1085 * @param array $msg_depth Array of messages depth indexed by message ID1086 * @param array $msg_children Array of messages children flags indexed by message ID1087 * @return array Message headers array indexed by messageID944 * @param array $headers Reference to headers array indexed by message UID 945 * @param rcube_imap_result $threads Threads data object 946 * 947 * @return array Message headers array indexed by message UID 1088 948 * @access private 1089 949 */ 1090 private function _set_thread_flags(&$headers, $ msg_depth, $msg_children)950 private function _set_thread_flags(&$headers, $threads) 1091 951 { 1092 952 $parents = array(); 1093 953 1094 foreach ($headers as $idx => $header) { 1095 $id = $header->id; 1096 $depth = $msg_depth[$id]; 954 list ($msg_depth, $msg_children) = $threads->getThreadData(); 955 956 foreach ($headers as $uid => $header) { 957 $depth = $msg_depth[$uid]; 1097 958 $parents = array_slice($parents, 0, $depth); 1098 959 1099 960 if (!empty($parents)) { 1100 $headers[$ idx]->parent_uid = end($parents);961 $headers[$uid]->parent_uid = end($parents); 1101 962 if (empty($header->flags['SEEN'])) 1102 963 $headers[$parents[0]]->unread_children++; 1103 964 } 1104 array_push($parents, $ header->uid);1105 1106 $headers[$ idx]->depth = $depth;1107 $headers[$ idx]->has_children = $msg_children[$id];965 array_push($parents, $uid); 966 967 $headers[$uid]->depth = $depth; 968 $headers[$uid]->has_children = $msg_children[$uid]; 1108 969 } 1109 970 } … … 1112 973 /** 1113 974 * Private method for listing a set of message headers (search results) 975 * 976 * @param string $mailbox Mailbox/folder name 977 * @param int $page Current page to list 978 * @param int $slice Number of slice items to extract from result array 979 * 980 * @return array Indexed array with message header objects 981 * @access private 982 */ 983 private function _list_header_set($mailbox, $page, $slice=0) 984 { 985 if (!strlen($mailbox) || empty($this->search_set) || $this->search_set->isEmpty()) { 986 return array(); 987 } 988 989 // use saved messages from searching 990 if ($this->threading) { 991 return $this->_list_thread_header_set($mailbox, $page, $slice); 992 } 993 994 // search set is threaded, we need a new one 995 if ($this->search_threads) { 996 $this->search('', $this->search_string, $this->search_charset, $this->sort_field); 997 } 998 999 $index = clone $this->search_set; 1000 $from = ($page-1) * $this->page_size; 1001 $to = $from + $this->page_size; 1002 1003 // return empty array if no messages found 1004 if ($index->isEmpty()) 1005 return array(); 1006 1007 // quickest method (default sorting) 1008 if (!$this->search_sort_field && !$this->sort_field) { 1009 $got_index = true; 1010 } 1011 // sorted messages, so we can first slice array and then fetch only wanted headers 1012 else if ($this->search_sorted) { // SORT searching result 1013 $got_index = true; 1014 // reset search set if sorting field has been changed 1015 if ($this->sort_field && $this->search_sort_field != $this->sort_field) { 1016 $this->search('', $this->search_string, $this->search_charset, $this->sort_field); 1017 1018 $index = clone $this->search_set; 1019 1020 // return empty array if no messages found 1021 if ($index->isEmpty()) 1022 return array(); 1023 } 1024 } 1025 1026 if ($got_index) { 1027 if ($this->sort_order != $index->getParameters('ORDER')) { 1028 $index->revert(); 1029 } 1030 1031 // get messages uids for one page 1032 $index->slice($from, $to-$from); 1033 1034 if ($slice) 1035 $index->slice(-$slice, $slice); 1036 1037 // fetch headers 1038 $a_index = $index->get(); 1039 $a_msg_headers = $this->fetch_headers($mailbox, $a_index); 1040 1041 return array_values($a_msg_headers); 1042 } 1043 1044 // SEARCH result, need sorting 1045 $cnt = $index->count(); 1046 1047 // 300: experimantal value for best result 1048 if (($cnt > 300 && $cnt > $this->page_size) || !$this->sort_field) { 1049 // use memory less expensive (and quick) method for big result set 1050 $index = clone $this->message_index('', $this->sort_field, $this->sort_order); 1051 // get messages uids for one page... 1052 $index->slice($start_msg, min($cnt-$from, $this->page_size)); 1053 1054 if ($slice) 1055 $index->slice(-$slice, $slice); 1056 1057 // ...and fetch headers 1058 $a_index = $index->get(); 1059 $a_msg_headers = $this->fetch_headers($mailbox, $a_index); 1060 1061 return array_values($a_msg_headers); 1062 } 1063 else { 1064 // for small result set we can fetch all messages headers 1065 $a_index = $index->get(); 1066 $a_msg_headers = $this->fetch_headers($mailbox, $a_index, false); 1067 1068 // return empty array if no messages found 1069 if (!is_array($a_msg_headers) || empty($a_msg_headers)) 1070 return array(); 1071 1072 // if not already sorted 1073 $a_msg_headers = $this->conn->sortHeaders( 1074 $a_msg_headers, $this->sort_field, $this->sort_order); 1075 1076 // only return the requested part of the set 1077 $a_msg_headers = array_slice(array_values($a_msg_headers), 1078 $from, min($cnt-$to, $this->page_size)); 1079 1080 if ($slice) 1081 $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice); 1082 1083 return $a_msg_headers; 1084 } 1085 } 1086 1087 1088 /** 1089 * Private method for listing a set of threaded message headers (search results) 1114 1090 * 1115 1091 * @param string $mailbox Mailbox/folder name 1116 1092 * @param int $page Current page to list 1117 * @param string $sort_field Header field to sort by 1118 * @param string $sort_order Sort order [ASC|DESC] 1119 * @param int $slice Number of slice items to extract from result array 1093 * @param int $slice Number of slice items to extract from result array 1094 * 1120 1095 * @return array Indexed array with message header objects 1121 1096 * @access private 1122 1097 * @see rcube_imap::list_header_set() 1123 1098 */ 1124 private function _list_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) 1125 { 1126 if (!strlen($mailbox) || empty($this->search_set)) 1099 private function _list_thread_header_set($mailbox, $page, $slice=0) 1100 { 1101 // update search_set if previous data was fetched with disabled threading 1102 if (!$this->search_threads) { 1103 if ($this->search_set->isEmpty()) 1104 return array(); 1105 $this->search('', $this->search_string, $this->search_charset, $this->sort_field); 1106 } 1107 1108 return $this->_fetch_thread_headers($mailbox, clone $this->search_set, $page, $slice); 1109 } 1110 1111 1112 /** 1113 * Fetches messages headers (by UID) 1114 * 1115 * @param string $mailbox Mailbox name 1116 * @param array $msgs Message UIDs 1117 * @param bool $sort Enables result sorting by $msgs 1118 * @param bool $force Disables cache use 1119 * 1120 * @return array Messages headers indexed by UID 1121 * @access private 1122 */ 1123 function fetch_headers($mailbox, $msgs, $sort = true, $force = false) 1124 { 1125 if (empty($msgs)) 1127 1126 return array(); 1128 1127 1129 // use saved messages from searching 1130 if ($this->threading) 1131 return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice); 1132 1133 // search set is threaded, we need a new one 1134 if ($this->search_threads) { 1135 if (empty($this->search_set['tree'])) 1136 return array(); 1137 $this->search('', $this->search_string, $this->search_charset, $sort_field); 1138 } 1139 1140 $msgs = $this->search_set; 1141 $a_msg_headers = array(); 1142 $page = $page ? $page : $this->list_page; 1143 $start_msg = ($page-1) * $this->page_size; 1144 1145 $this->_set_sort_order($sort_field, $sort_order); 1146 1147 // quickest method (default sorting) 1148 if (!$this->search_sort_field && !$this->sort_field) { 1149 if ($sort_order == 'DESC') 1150 $msgs = array_reverse($msgs); 1151 1152 // get messages uids for one page 1153 $msgs = array_slice(array_values($msgs), $start_msg, min(count($msgs)-$start_msg, $this->page_size)); 1154 1155 if ($slice) 1156 $msgs = array_slice($msgs, -$slice, $slice); 1157 1158 // fetch headers 1159 $a_msg_headers = $this->fetch_headers($mailbox, $msgs); 1160 1161 // I didn't found in RFC that FETCH always returns messages sorted by index 1128 if (!$force && ($mcache = $this->get_mcache_engine())) { 1129 $headers = $mcache->get_messages($mailbox, $msgs); 1130 } 1131 else { 1132 // fetch reqested headers from server 1133 $headers = $this->conn->fetchHeaders( 1134 $mailbox, $msgs, true, false, $this->get_fetch_headers()); 1135 } 1136 1137 if (empty($headers)) 1138 return array(); 1139 1140 foreach ($headers as $h) { 1141 $a_msg_headers[$h->uid] = $h; 1142 } 1143 1144 if ($sort) { 1145 // use this class for message sorting 1162 1146 $sorter = new rcube_header_sorter(); 1163 1147 $sorter->set_index($msgs); 1164 1148 $sorter->sort_headers($a_msg_headers); 1165 1166 return array_values($a_msg_headers);1167 }1168 1169 // sorted messages, so we can first slice array and then fetch only wanted headers1170 if ($this->search_sorted) { // SORT searching result1171 // reset search set if sorting field has been changed1172 if ($this->sort_field && $this->search_sort_field != $this->sort_field)1173 $msgs = $this->search('', $this->search_string, $this->search_charset, $this->sort_field);1174 1175 // return empty array if no messages found1176 if (empty($msgs))1177 return array();1178 1179 if ($sort_order == 'DESC')1180 $msgs = array_reverse($msgs);1181 1182 // get messages uids for one page1183 $msgs = array_slice(array_values($msgs), $start_msg, min(count($msgs)-$start_msg, $this->page_size));1184 1185 if ($slice)1186 $msgs = array_slice($msgs, -$slice, $slice);1187 1188 // fetch headers1189 $a_msg_headers = $this->fetch_headers($mailbox, $msgs);1190 1191 $sorter = new rcube_header_sorter();1192 $sorter->set_index($msgs);1193 $sorter->sort_headers($a_msg_headers);1194 1195 return array_values($a_msg_headers);1196 }1197 else { // SEARCH result, need sorting1198 $cnt = count($msgs);1199 // 300: experimantal value for best result1200 if (($cnt > 300 && $cnt > $this->page_size) || !$this->sort_field) {1201 // use memory less expensive (and quick) method for big result set1202 $a_index = $this->message_index('', $this->sort_field, $this->sort_order);1203 // get messages uids for one page...1204 $msgs = array_slice($a_index, $start_msg, min($cnt-$start_msg, $this->page_size));1205 if ($slice)1206 $msgs = array_slice($msgs, -$slice, $slice);1207 // ...and fetch headers1208 $a_msg_headers = $this->fetch_headers($mailbox, $msgs);1209 1210 1211 // return empty array if no messages found1212 if (!is_array($a_msg_headers) || empty($a_msg_headers))1213 return array();1214 1215 $sorter = new rcube_header_sorter();1216 $sorter->set_index($msgs);1217 $sorter->sort_headers($a_msg_headers);1218 1219 return array_values($a_msg_headers);1220 }1221 else {1222 // for small result set we can fetch all messages headers1223 $a_msg_headers = $this->fetch_headers($mailbox, $msgs);1224 1225 // return empty array if no messages found1226 if (!is_array($a_msg_headers) || empty($a_msg_headers))1227 return array();1228 1229 // if not already sorted1230 $a_msg_headers = $this->conn->sortHeaders(1231 $a_msg_headers, $this->sort_field, $this->sort_order);1232 1233 // only return the requested part of the set1234 $a_msg_headers = array_slice(array_values($a_msg_headers),1235 $start_msg, min($cnt-$start_msg, $this->page_size));1236 1237 if ($slice)1238 $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice);1239 1240 return $a_msg_headers;1241 }1242 }1243 }1244 1245 1246 /**1247 * Private method for listing a set of threaded message headers (search results)1248 *1249 * @param string $mailbox Mailbox/folder name1250 * @param int $page Current page to list1251 * @param string $sort_field Header field to sort by1252 * @param string $sort_order Sort order [ASC|DESC]1253 * @param int $slice Number of slice items to extract from result array1254 * @return array Indexed array with message header objects1255 * @access private1256 * @see rcube_imap::list_header_set()1257 */1258 private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)1259 {1260 // update search_set if previous data was fetched with disabled threading1261 if (!$this->search_threads) {1262 if (empty($this->search_set))1263 return array();1264 $this->search('', $this->search_string, $this->search_charset, $sort_field);1265 }1266 1267 // empty result1268 if (empty($this->search_set['tree']))1269 return array();1270 1271 $thread_tree = $this->search_set['tree'];1272 $msg_depth = $this->search_set['depth'];1273 $has_children = $this->search_set['children'];1274 $a_msg_headers = array();1275 1276 $page = $page ? $page : $this->list_page;1277 $start_msg = ($page-1) * $this->page_size;1278 1279 $this->_set_sort_order($sort_field, $sort_order);1280 1281 $msg_index = $this->sort_threads($mailbox, $thread_tree, array_keys($msg_depth));1282 1283 return $this->_fetch_thread_headers($mailbox,1284 $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0);1285 }1286 1287 1288 /**1289 * Helper function to get first and last index of the requested set1290 *1291 * @param int $max Messages count1292 * @param mixed $page Page number to show, or string 'all'1293 * @return array Array with two values: first index, last index1294 * @access private1295 */1296 private function _get_message_range($max, $page)1297 {1298 $start_msg = ($page-1) * $this->page_size;1299 1300 if ($page=='all') {1301 $begin = 0;1302 $end = $max;1303 }1304 else if ($this->sort_order=='DESC') {1305 $begin = $max - $this->page_size - $start_msg;1306 $end = $max - $start_msg;1307 }1308 else {1309 $begin = $start_msg;1310 $end = $start_msg + $this->page_size;1311 }1312 1313 if ($begin < 0) $begin = 0;1314 if ($end < 0) $end = $max;1315 if ($end > $max) $end = $max;1316 1317 return array($begin, $end);1318 }1319 1320 1321 /**1322 * Fetches messages headers1323 *1324 * @param string $mailbox Mailbox name1325 * @param array $msgs Messages sequence numbers1326 * @param bool $is_uid Enable if $msgs numbers are UIDs1327 * @param bool $force Disables cache use1328 *1329 * @return array Messages headers indexed by UID1330 * @access private1331 */1332 function fetch_headers($mailbox, $msgs, $is_uid = false, $force = false)1333 {1334 if (empty($msgs))1335 return array();1336 1337 if (!$force && ($mcache = $this->get_mcache_engine())) {1338 return $mcache->get_messages($mailbox, $msgs, $is_uid);1339 }1340 1341 // fetch reqested headers from server1342 $index = $this->conn->fetchHeaders(1343 $mailbox, $msgs, $is_uid, false, $this->get_fetch_headers());1344 1345 if (empty($index))1346 return array();1347 1348 foreach ($index as $headers) {1349 $a_msg_headers[$headers->uid] = $headers;1350 1149 } 1351 1150 … … 1363 1162 * @return int Folder status 1364 1163 */ 1365 function mailbox_status($mailbox = null)1164 public function mailbox_status($mailbox = null) 1366 1165 { 1367 1166 if (!strlen($mailbox)) { … … 1426 1225 1427 1226 /** 1428 * Return sorted array of message IDs (not UIDs)1227 * Return sorted list of message UIDs 1429 1228 * 1430 1229 * @param string $mailbox Mailbox to get index from 1431 1230 * @param string $sort_field Sort column 1432 1231 * @param string $sort_order Sort order [ASC, DESC] 1433 * @return array Indexed array with message IDs 1434 */ 1435 function message_index($mailbox='', $sort_field=NULL, $sort_order=NULL) 1232 * 1233 * @return rcube_result_index|rcube_result_thread List of messages (UIDs) 1234 */ 1235 public function message_index($mailbox='', $sort_field=NULL, $sort_order=NULL) 1436 1236 { 1437 1237 if ($this->threading) 1438 1238 return $this->thread_index($mailbox, $sort_field, $sort_order); 1439 1239 1440 $this-> _set_sort_order($sort_field, $sort_order);1240 $this->set_sort_order($sort_field, $sort_order); 1441 1241 1442 1242 if (!strlen($mailbox)) { 1443 1243 $mailbox = $this->mailbox; 1444 1244 } 1445 $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.msgi";1446 1245 1447 1246 // we have a saved search result, get index from there 1448 if (!isset($this->icache[$key]) && $this->search_string 1449 && !$this->search_threads && $mailbox == $this->mailbox) { 1247 if ($this->search_string) { 1248 if ($this->search_threads) { 1249 $this->search($mailbox, $this->search_string, $this->search_charset, $this->sort_field); 1250 } 1251 1450 1252 // use message index sort as default sorting 1451 if (!$this->sort_field) { 1452 $msgs = $this->search_set; 1453 1454 if ($this->search_sort_field != 'date') 1455 sort($msgs); 1456 1457 if ($this->sort_order == 'DESC') 1458 $this->icache[$key] = array_reverse($msgs); 1459 else 1460 $this->icache[$key] = $msgs; 1461 } 1462 // sort with SORT command 1463 else if ($this->search_sorted) { 1464 if ($this->sort_field && $this->search_sort_field != $this->sort_field) 1465 $this->search('', $this->search_string, $this->search_charset, $this->sort_field); 1466 1467 if ($this->sort_order == 'DESC') 1468 $this->icache[$key] = array_reverse($this->search_set); 1469 else 1470 $this->icache[$key] = $this->search_set; 1253 if (!$this->sort_field || $this->search_sorted) { 1254 if ($this->sort_field && $this->search_sort_field != $this->sort_field) { 1255 $this->search($mailbox, $this->search_string, $this->search_charset, $this->sort_field); 1256 } 1257 $index = $this->search_set; 1471 1258 } 1472 1259 else { 1473 $a_index = $this->conn->fetchHeaderIndex($mailbox, 1474 join(',', $this->search_set), $this->sort_field, $this->skip_deleted); 1475 1476 if (is_array($a_index)) { 1477 if ($this->sort_order=="ASC") 1478 asort($a_index); 1479 else if ($this->sort_order=="DESC") 1480 arsort($a_index); 1481 1482 $this->icache[$key] = array_keys($a_index); 1483 } 1484 else { 1485 $this->icache[$key] = array(); 1486 } 1487 } 1488 } 1489 1490 // have stored it in RAM 1491 if (isset($this->icache[$key])) 1492 return $this->icache[$key]; 1260 $index = $this->conn->index($mailbox, $this->search_set->get(), 1261 $this->sort_field, $this->skip_deleted, true, true); 1262 } 1263 1264 if ($this->sort_order != $index->getParameters('ORDER')) { 1265 $index->revert(); 1266 } 1267 1268 return $index; 1269 } 1493 1270 1494 1271 // check local cache 1495 1272 if ($mcache = $this->get_mcache_engine()) { 1496 $a_index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order); 1497 $this->icache[$key] = array_keys($a_index); 1273 $index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order); 1498 1274 } 1499 1275 // fetch from IMAP server 1500 1276 else { 1501 $ this->icache[$key]= $this->message_index_direct(1277 $index = $this->message_index_direct( 1502 1278 $mailbox, $this->sort_field, $this->sort_order); 1503 1279 } 1504 1280 1505 return $ this->icache[$key];1506 } 1507 1508 1509 /** 1510 * Return sorted array of message IDs (not UIDs) directly from IMAP server.1511 * Doesn't use cache and ignores current search settings.1281 return $index; 1282 } 1283 1284 1285 /** 1286 * Return sorted list of message UIDs ignoring current search settings. 1287 * Doesn't uses cache by default. 1512 1288 * 1513 1289 * @param string $mailbox Mailbox to get index from 1514 1290 * @param string $sort_field Sort column 1515 1291 * @param string $sort_order Sort order [ASC, DESC] 1516 * 1517 * @return array Indexed array with message IDs 1518 */ 1519 function message_index_direct($mailbox, $sort_field = null, $sort_order = null) 1520 { 1292 * @param bool $skip_cache Disables cache usage 1293 * 1294 * @return rcube_result_index Sorted list of message UIDs 1295 */ 1296 public function message_index_direct($mailbox, $sort_field = null, $sort_order = null, $skip_cache = true) 1297 { 1298 if (!$skip_cache && ($mcache = $this->get_mcache_engine())) { 1299 $index = $mcache->get_index($mailbox, $sort_field, $sort_order); 1300 } 1521 1301 // use message index sort as default sorting 1522 if (!$sort_field) { 1523 if ($this->skip_deleted) { 1524 $a_index = $this->conn->search($mailbox, 'ALL UNDELETED'); 1525 // I didn't found that SEARCH should return sorted IDs 1526 if (is_array($a_index)) 1527 sort($a_index); 1528 } else if ($max = $this->_messagecount($mailbox, 'ALL', true, false)) { 1529 $a_index = range(1, $max); 1530 } 1531 1532 if ($a_index !== false && $sort_order == 'DESC') 1533 $a_index = array_reverse($a_index); 1302 else if (!$sort_field) { 1303 if ($this->skip_deleted && !empty($this->icache['undeleted_idx']) 1304 && $this->icache['undeleted_idx']->getParameters('MAILBOX') == $mailbox 1305 ) { 1306 $index = $this->icache['undeleted_idx']; 1307 } 1308 else { 1309 $index = $this->conn->search($mailbox, 1310 'ALL' .($this->skip_deleted ? ' UNDELETED' : ''), true); 1311 } 1534 1312 } 1535 1313 // fetch complete message index 1536 else if ($this->get_capability('SORT') && 1537 ($a_index = $this->conn->sort($mailbox, 1538 $sort_field, $this->skip_deleted ? 'UNDELETED' : '')) !== false 1539 ) { 1540 if ($sort_order == 'DESC') 1541 $a_index = array_reverse($a_index); 1542 } 1543 else if ($a_index = $this->conn->fetchHeaderIndex( 1544 $mailbox, "1:*", $sort_field, $skip_deleted)) { 1545 if ($sort_order=="ASC") 1546 asort($a_index); 1547 else if ($sort_order=="DESC") 1548 arsort($a_index); 1549 1550 $a_index = array_keys($a_index); 1551 } 1552 1553 return $a_index !== false ? $a_index : array(); 1554 } 1555 1556 1557 /** 1558 * Return sorted array of threaded message IDs (not UIDs) 1314 else { 1315 if ($this->get_capability('SORT')) { 1316 $index = $this->conn->sort($mailbox, $sort_field, 1317 $this->skip_deleted ? 'UNDELETED' : '', true); 1318 } 1319 1320 if (empty($index) || $index->isError()) { 1321 $index = $this->conn->index($mailbox, "1:*", $sort_field, 1322 $this->skip_deleted, false, true); 1323 } 1324 } 1325 1326 if ($sort_order != $index->getParameters('ORDER')) { 1327 $index->revert(); 1328 } 1329 1330 return $index; 1331 } 1332 1333 1334 /** 1335 * Return index of threaded message UIDs 1559 1336 * 1560 1337 * @param string $mailbox Mailbox to get index from 1561 1338 * @param string $sort_field Sort column 1562 1339 * @param string $sort_order Sort order [ASC, DESC] 1563 * @return array Indexed array with message IDs 1340 * 1341 * @return rcube_result_thread Message UIDs 1564 1342 */ 1565 1343 function thread_index($mailbox='', $sort_field=NULL, $sort_order=NULL) 1566 1344 { 1567 $this->_set_sort_order($sort_field, $sort_order);1568 1569 1345 if (!strlen($mailbox)) { 1570 1346 $mailbox = $this->mailbox; 1571 1347 } 1572 $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.thi";1573 1348 1574 1349 // we have a saved search result, get index from there 1575 if (!isset($this->icache[$key]) && $this->search_string 1576 && $this->search_threads && $mailbox == $this->mailbox) { 1577 // use message IDs for better performance 1578 $ids = array_keys_recursive($this->search_set['tree']); 1579 $this->icache[$key] = $this->_flatten_threads($mailbox, $this->search_set['tree'], $ids); 1580 } 1581 1582 // have stored it in RAM 1583 if (isset($this->icache[$key])) 1584 return $this->icache[$key]; 1585 1586 // get all threads (default sort order) 1587 list ($thread_tree) = $this->fetch_threads($mailbox); 1588 1589 $this->icache[$key] = $this->_flatten_threads($mailbox, $thread_tree); 1590 1591 return $this->icache[$key]; 1592 } 1593 1594 1595 /** 1596 * Return array of threaded messages (all, not only roots) 1597 * 1598 * @param string $mailbox Mailbox to get index from 1599 * @param array $thread_tree Threaded messages array (see fetch_threads()) 1600 * @param array $ids Message IDs if we know what we need (e.g. search result) 1601 * for better performance 1602 * @return array Indexed array with message IDs 1603 * 1604 * @access private 1605 */ 1606 private function _flatten_threads($mailbox, $thread_tree, $ids=null) 1607 { 1608 if (empty($thread_tree)) 1609 return array(); 1610 1611 $msg_index = $this->sort_threads($mailbox, $thread_tree, $ids); 1612 1613 if ($this->sort_order == 'DESC') 1614 $msg_index = array_reverse($msg_index); 1615 1616 // flatten threads array 1617 $all_ids = array(); 1618 foreach ($msg_index as $root) { 1619 $all_ids[] = $root; 1620 if (!empty($thread_tree[$root])) { 1621 foreach (array_keys_recursive($thread_tree[$root]) as $val) 1622 $all_ids[] = $val; 1623 } 1624 } 1625 1626 return $all_ids; 1350 if ($this->search_string && $this->search_threads && $mailbox == $this->mailbox) { 1351 $threads = $this->search_set; 1352 } 1353 else { 1354 // get all threads (default sort order) 1355 $threads = $this->fetch_threads($mailbox); 1356 } 1357 1358 $this->set_sort_order($sort_field, $sort_order); 1359 $this->sort_threads($threads); 1360 1361 return $threads; 1362 } 1363 1364 1365 /** 1366 * Sort threaded result, using THREAD=REFS method 1367 * 1368 * @param rcube_result_thread $threads Threads result set 1369 */ 1370 private function sort_threads($threads) 1371 { 1372 if ($threads->isEmpty()) { 1373 return; 1374 } 1375 1376 // THREAD=ORDEREDSUBJECT: sorting by sent date of root message 1377 // THREAD=REFERENCES: sorting by sent date of root message 1378 // THREAD=REFS: sorting by the most recent date in each thread 1379 1380 if ($this->sort_field && ($this->sort_field != 'date' || $this->get_capability('THREAD') != 'REFS')) { 1381 $index = $this->message_index_direct($this->mailbox, $this->sort_field, $this->sort_order, false); 1382 1383 if (!$index->isEmpty()) { 1384 $threads->sort($index); 1385 } 1386 } 1387 else { 1388 if ($this->sort_order != $threads->getParameters('ORDER')) { 1389 $threads->revert(); 1390 } 1391 } 1627 1392 } 1628 1393 … … 1635 1400 * @param string $charset Search charset 1636 1401 * @param string $sort_field Header field to sort by 1637 * @return array search results as list of message IDs1638 1402 * @access public 1639 1403 */ 1640 function search($mailbox='', $str= NULL, $charset=NULL, $sort_field=NULL)1404 function search($mailbox='', $str='ALL', $charset=NULL, $sort_field=NULL) 1641 1405 { 1642 1406 if (!$str) 1643 return false;1407 $str = 'ALL'; 1644 1408 1645 1409 if (!strlen($mailbox)) { … … 1649 1413 $results = $this->_search_index($mailbox, $str, $charset, $sort_field); 1650 1414 1651 $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading,1415 $this->set_search_set($str, $results, $charset, $sort_field, 1652 1416 $this->threading || $this->search_sorted ? true : false); 1653 1654 return $results;1655 1417 } 1656 1418 … … 1664 1426 * @param string $sort_field Sorting field 1665 1427 * 1666 * @return array search results as list of message ids1428 * @return rcube_result_index|rcube_result_thread Search results (UIDs) 1667 1429 * @see rcube_imap::search() 1668 1430 */ … … 1675 1437 1676 1438 if ($this->threading) { 1677 $ a_messages = $this->conn->thread($mailbox, $this->threading, $criteria, $charset);1439 $threads = $this->conn->thread($mailbox, $this->threading, $criteria, true, $charset); 1678 1440 1679 1441 // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, 1680 1442 // but I've seen that Courier doesn't support UTF-8) 1681 if ($a_messages === false && $charset && $charset != 'US-ASCII') 1682 $a_messages = $this->conn->thread($mailbox, $this->threading, 1683 $this->convert_criteria($criteria, $charset), 'US-ASCII'); 1684 1685 if ($a_messages !== false) { 1686 list ($thread_tree, $msg_depth, $has_children) = $a_messages; 1687 $a_messages = array( 1688 'tree' => $thread_tree, 1689 'depth'=> $msg_depth, 1690 'children' => $has_children 1691 ); 1692 } 1693 1694 return $a_messages; 1443 if ($threads->isError() && $charset && $charset != 'US-ASCII') 1444 $threads = $this->conn->thread($mailbox, $this->threading, 1445 $this->convert_criteria($criteria, $charset), true, 'US-ASCII'); 1446 1447 return $threads; 1695 1448 } 1696 1449 1697 1450 if ($sort_field && $this->get_capability('SORT')) { 1698 $charset = $charset ? $charset : $this->default_charset;1699 $ a_messages = $this->conn->sort($mailbox, $sort_field, $criteria, false, $charset);1451 $charset = $charset ? $charset : $this->default_charset; 1452 $messages = $this->conn->sort($mailbox, $sort_field, $criteria, true, $charset); 1700 1453 1701 1454 // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, 1702 1455 // but I've seen Courier with disabled UTF-8 support) 1703 if ($ a_messages === false&& $charset && $charset != 'US-ASCII')1704 $ a_messages = $this->conn->sort($mailbox, $sort_field,1705 $this->convert_criteria($criteria, $charset), false, 'US-ASCII');1706 1707 if ( $a_messages !== false) {1456 if ($messages->isError() && $charset && $charset != 'US-ASCII') 1457 $messages = $this->conn->sort($mailbox, $sort_field, 1458 $this->convert_criteria($criteria, $charset), true, 'US-ASCII'); 1459 1460 if (!$messages->isError()) { 1708 1461 $this->search_sorted = true; 1709 return $a_messages; 1710 } 1711 } 1712 1713 if ($orig_criteria == 'ALL') { 1714 $max = $this->_messagecount($mailbox, 'ALL', true, false); 1715 $a_messages = $max ? range(1, $max) : array(); 1716 } 1717 else { 1718 $a_messages = $this->conn->search($mailbox, 1719 ($charset ? "CHARSET $charset " : '') . $criteria); 1720 1721 // Error, try with US-ASCII (some servers may support only US-ASCII) 1722 if ($a_messages === false && $charset && $charset != 'US-ASCII') 1723 $a_messages = $this->conn->search($mailbox, 1724 'CHARSET US-ASCII ' . $this->convert_criteria($criteria, $charset)); 1725 1726 // I didn't found that SEARCH should return sorted IDs 1727 if (is_array($a_messages) && !$this->sort_field) 1728 sort($a_messages); 1729 } 1462 return $messages; 1463 } 1464 } 1465 1466 $messages = $this->conn->search($mailbox, 1467 ($charset ? "CHARSET $charset " : '') . $criteria, true); 1468 1469 // Error, try with US-ASCII (some servers may support only US-ASCII) 1470 if ($messages->isError() && $charset && $charset != 'US-ASCII') 1471 $messages = $this->conn->search($mailbox, 1472 'CHARSET US-ASCII ' . $this->convert_criteria($criteria, $charset), true); 1730 1473 1731 1474 $this->search_sorted = false; 1732 1475 1733 return $ a_messages;1476 return $messages; 1734 1477 } 1735 1478 … … 1743 1486 * @param boolean $ret_uid True if UIDs should be returned 1744 1487 * 1745 * @return array Search results as list of message IDs or UIDs1746 */ 1747 function search_once($mailbox='', $str= NULL, $ret_uid=false)1488 * @return rcube_result_index Search result (UIDs) 1489 */ 1490 function search_once($mailbox='', $str='ALL') 1748 1491 { 1749 1492 if (!$str) 1750 return false;1493 return 'ALL'; 1751 1494 1752 1495 if (!strlen($mailbox)) { … … 1754 1497 } 1755 1498 1756 return $this->conn->search($mailbox, $str, $ret_uid); 1499 $index = $this->conn->search($mailbox, $str, true); 1500 1501 return $index; 1757 1502 } 1758 1503 … … 1792 1537 1793 1538 /** 1794 * Sort thread1795 *1796 * @param string $mailbox Mailbox name1797 * @param array $thread_tree Unsorted thread tree (rcube_imap_generic::thread() result)1798 * @param array $ids Message IDs if we know what we need (e.g. search result)1799 *1800 * @return array Sorted roots IDs1801 */1802 function sort_threads($mailbox, $thread_tree, $ids = null)1803 {1804 // THREAD=ORDEREDSUBJECT: sorting by sent date of root message1805 // THREAD=REFERENCES: sorting by sent date of root message1806 // THREAD=REFS: sorting by the most recent date in each thread1807 1808 // default sorting1809 if (!$this->sort_field || ($this->sort_field == 'date' && $this->threading == 'REFS')) {1810 return array_keys((array)$thread_tree);1811 }1812 // here we'll implement REFS sorting1813 else {1814 if ($mcache = $this->get_mcache_engine()) {1815 $a_index = $mcache->get_index($mailbox, $this->sort_field, 'ASC');1816 if (is_array($a_index)) {1817 $a_index = array_keys($a_index);1818 // now we must remove IDs that doesn't exist in $ids1819 if (!empty($ids))1820 $a_index = array_intersect($a_index, $ids);1821 }1822 }1823 // use SORT command1824 else if ($this->get_capability('SORT') &&1825 ($a_index = $this->conn->sort($mailbox, $this->sort_field,1826 !empty($ids) ? $ids : ($this->skip_deleted ? 'UNDELETED' : ''))) !== false1827 ) {1828 // do nothing1829 }1830 else {1831 // fetch specified headers for all messages and sort them1832 $a_index = $this->conn->fetchHeaderIndex($mailbox, !empty($ids) ? $ids : "1:*",1833 $this->sort_field, $this->skip_deleted);1834 1835 // return unsorted tree if we've got no index data1836 if (!empty($a_index)) {1837 asort($a_index); // ASC1838 $a_index = array_values($a_index);1839 }1840 }1841 1842 if (empty($a_index))1843 return array_keys((array)$thread_tree);1844 1845 return $this->_sort_thread_refs($thread_tree, $a_index);1846 }1847 }1848 1849 1850 /**1851 * THREAD=REFS sorting implementation1852 *1853 * @param array $tree Thread tree array (message identifiers as keys)1854 * @param array $index Array of sorted message identifiers1855 *1856 * @return array Array of sorted roots messages1857 */1858 private function _sort_thread_refs($tree, $index)1859 {1860 if (empty($tree))1861 return array();1862 1863 $index = array_combine(array_values($index), $index);1864 1865 // assign roots1866 foreach ($tree as $idx => $val) {1867 $index[$idx] = $idx;1868 if (!empty($val)) {1869 $idx_arr = array_keys_recursive($tree[$idx]);1870 foreach ($idx_arr as $subidx)1871 $index[$subidx] = $idx;1872 }1873 }1874 1875 $index = array_values($index);1876 1877 // create sorted array of roots1878 $msg_index = array();1879 if ($this->sort_order != 'DESC') {1880 foreach ($index as $idx)1881 if (!isset($msg_index[$idx]))1882 $msg_index[$idx] = $idx;1883 $msg_index = array_values($msg_index);1884 }1885 else {1886 for ($x=count($index)-1; $x>=0; $x--)1887 if (!isset($msg_index[$index[$x]]))1888 $msg_index[$index[$x]] = $index[$x];1889 $msg_index = array_reverse($msg_index);1890 }1891 1892 return $msg_index;1893 }1894 1895 1896 /**1897 1539 * Refresh saved search set 1898 1540 * … … 1901 1543 function refresh_search() 1902 1544 { 1903 if (!empty($this->search_string)) 1904 $this->search _set = $this->search('', $this->search_string, $this->search_charset,1905 $this->search_sort_field, $this->search_threads, $this->search_sorted);1545 if (!empty($this->search_string)) { 1546 $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field); 1547 } 1906 1548 1907 1549 return $this->get_search_set(); … … 1910 1552 1911 1553 /** 1912 * Check if the given message ID is part of the current search set 1913 * 1914 * @param string $msgid Message id 1554 * Check if the given message UID is part of the current search set 1555 * 1556 * @param string $msgid Message UID 1557 * 1915 1558 * @return boolean True on match or if no search request is stored 1916 1559 */ 1917 function in_searchset($ msgid)1560 function in_searchset($uid) 1918 1561 { 1919 1562 if (!empty($this->search_string)) { 1920 if ($this->search_threads) 1921 return isset($this->search_set['depth']["$msgid"]); 1922 else 1923 return in_array("$msgid", (array)$this->search_set, true); 1924 } 1925 else 1926 return true; 1563 return $this->search_set->exists($uid); 1564 } 1565 return true; 1927 1566 } 1928 1567 … … 1982 1621 // message doesn't exist? 1983 1622 if (empty($headers)) 1984 return null; 1623 return null; 1985 1624 1986 1625 // structure might be cached … … 2657 2296 if ($this->search_threads || $all_mode) 2658 2297 $this->refresh_search(); 2659 else { 2660 $a_uids = explode(',', $uids); 2661 foreach ($a_uids as $uid) 2662 $a_mids[] = $this->uid2id($uid, $from_mbox); 2663 $this->search_set = array_diff($this->search_set, $a_mids); 2664 } 2665 unset($a_mids); 2666 unset($a_uids); 2298 else 2299 $this->search_set->filter(explode(',', $uids)); 2667 2300 } 2668 2301 … … 2757 2390 if ($this->search_threads || $all_mode) 2758 2391 $this->refresh_search(); 2759 else { 2760 $a_uids = explode(',', $uids); 2761 foreach ($a_uids as $uid) 2762 $a_mids[] = $this->uid2id($uid, $mailbox); 2763 $this->search_set = array_diff($this->search_set, $a_mids); 2764 unset($a_uids); 2765 unset($a_mids); 2766 } 2392 else 2393 $this->search_set->filter(explode(',', $uids)); 2767 2394 } 2768 2395 … … 2883 2510 } 2884 2511 // get UIDs from current search set 2885 // @TODO: skip fetchUIDs() and work with IDs instead of UIDs (?)2886 2512 else { 2887 if ($this->search_threads) 2888 $uids = $this->conn->fetchUIDs($mailbox, array_keys($this->search_set['depth'])); 2889 else 2890 $uids = $this->conn->fetchUIDs($mailbox, $this->search_set); 2891 2892 // save ID-to-UID mapping in local cache 2893 if (is_array($uids)) 2894 foreach ($uids as $id => $uid) 2895 $this->uid_id_map[$mailbox][$uid] = $id; 2896 2897 $uids = join(',', $uids); 2513 $uids = join(',', $this->search_set->get()); 2898 2514 } 2899 2515 } … … 2908 2524 return array($uids, (bool) $all); 2909 2525 } 2910 2911 2912 /**2913 * Translate UID to message ID2914 *2915 * @param int $uid Message UID2916 * @param string $mailbox Mailbox name2917 *2918 * @return int Message ID2919 */2920 function get_id($uid, $mailbox=null)2921 {2922 if (!strlen($mailbox)) {2923 $mailbox = $this->mailbox;2924 }2925 2926 return $this->uid2id($uid, $mailbox);2927 }2928 2929 2930 /**2931 * Translate message number to UID2932 *2933 * @param int $id Message ID2934 * @param string $mailbox Mailbox name2935 *2936 * @return int Message UID2937 */2938 function get_uid($id, $mailbox=null)2939 {2940 if (!strlen($mailbox)) {2941 $mailbox = $this->mailbox;2942 }2943 2944 return $this->id2uid($id, $mailbox);2945 }2946 2947 2526 2948 2527 … … 3593 3172 3594 3173 // add (E)SEARCH result for ALL UNDELETED query 3595 if (!empty($this->icache['undeleted_idx']) && $this->icache['undeleted_idx'][0] == $mailbox) { 3596 $data['ALL_UNDELETED'] = $this->icache['undeleted_idx'][1]; 3597 $data['COUNT_UNDELETED'] = $this->icache['undeleted_idx'][2]; 3174 if (!empty($this->icache['undeleted_idx']) 3175 && $this->icache['undeleted_idx']->getParameters('MAILBOX') == $mailbox 3176 ) { 3177 $data['UNDELETED'] = $this->icache['undeleted_idx']; 3598 3178 } 3599 3179 … … 4269 3849 4270 3850 4271 /**4272 * Convert body charset to RCMAIL_CHARSET according to the ctype_parameters4273 *4274 * @param string $body Part body to decode4275 * @param string $ctype_param Charset to convert from4276 * @return string Content converted to internal charset4277 */4278 function charset_decode($body, $ctype_param)4279 {4280 if (is_array($ctype_param) && !empty($ctype_param['charset']))4281 return rcube_charset_convert($body, $ctype_param['charset']);4282 4283 // defaults to what is specified in the class header4284 return rcube_charset_convert($body, $this->default_charset);4285 }4286 4287 4288 3851 /* -------------------------------- 4289 3852 * private methods … … 4297 3860 * @access private 4298 3861 */ 4299 private function _set_sort_order($sort_field, $sort_order)3862 private function set_sort_order($sort_field, $sort_order) 4300 3863 { 4301 3864 if ($sort_field != null) … … 4367 3930 4368 3931 /** 4369 * Find s message sequence ID for specified UID4370 * 4371 * @param int $ uid Message UID3932 * Find UID of the specified message sequence ID 3933 * 3934 * @param int $id Message (sequence) ID 4372 3935 * @param string $mailbox Mailbox name 4373 * @param bool $force True to skip cache 4374 * 4375 * @return int Message (sequence) ID 4376 */ 4377 function uid2id($uid, $mailbox = null, $force = false) 3936 * 3937 * @return int Message UID 3938 */ 3939 function id2uid($id, $mailbox = null) 4378 3940 { 4379 3941 if (!strlen($mailbox)) { … … 4381 3943 } 4382 3944 4383 if (!empty($this->uid_id_map[$mailbox][$uid])) {4384 return $this->uid_id_map[$mailbox][$uid];4385 }4386 4387 if (!$force && ($mcache = $this->get_mcache_engine()))4388 $id = $mcache->uid2id($mailbox, $uid);4389 4390 if (empty($id))4391 $id = $this->conn->UID2ID($mailbox, $uid);4392 4393 $this->uid_id_map[$mailbox][$uid] = $id;4394 4395 return $id;4396 }4397 4398 4399 /**4400 * Find UID of the specified message sequence ID4401 *4402 * @param int $id Message (sequence) ID4403 * @param string $mailbox Mailbox name4404 * @param bool $force True to skip cache4405 *4406 * @return int Message UID4407 */4408 function id2uid($id, $mailbox = null, $force = false)4409 {4410 if (!strlen($mailbox)) {4411 $mailbox = $this->mailbox;4412 }4413 4414 3945 if ($uid = array_search($id, (array)$this->uid_id_map[$mailbox])) { 4415 3946 return $uid; 4416 3947 } 4417 3948 4418 if (!$force && ($mcache = $this->get_mcache_engine())) 4419 $uid = $mcache->id2uid($mailbox, $id); 4420 4421 if (empty($uid)) 4422 $uid = $this->conn->ID2UID($mailbox, $id); 3949 $uid = $this->conn->ID2UID($mailbox, $id); 4423 3950 4424 3951 $this->uid_id_map[$mailbox][$uid] = $id; … … 4706 4233 class rcube_header_sorter 4707 4234 { 4708 private $seqs = array();4709 4235 private $uids = array(); 4710 4236 … … 4713 4239 * Set the predetermined sort order. 4714 4240 * 4715 * @param array $index Numerically indexed array of IMAP ID or UIDs 4716 * @param bool $is_uid Set to true if $index contains UIDs 4717 */ 4718 function set_index($index, $is_uid = false) 4241 * @param array $index Numerically indexed array of IMAP UIDs 4242 */ 4243 function set_index($index) 4719 4244 { 4720 4245 $index = array_flip($index); 4721 4246 4722 if ($is_uid) 4723 $this->uids = $index; 4724 else 4725 $this->seqs = $index; 4247 $this->uids = $index; 4726 4248 } 4727 4249 … … 4733 4255 function sort_headers(&$headers) 4734 4256 { 4735 if (!empty($this->uids)) 4736 uksort($headers, array($this, "compare_uids")); 4737 else 4738 uasort($headers, array($this, "compare_seqnums")); 4739 } 4740 4741 /** 4742 * Sort method called by uasort() 4743 * 4744 * @param rcube_mail_header $a 4745 * @param rcube_mail_header $b 4746 */ 4747 function compare_seqnums($a, $b) 4748 { 4749 // First get the sequence number from the header object (the 'id' field). 4750 $seqa = $a->id; 4751 $seqb = $b->id; 4752 4753 // then find each sequence number in my ordered list 4754 $posa = isset($this->seqs[$seqa]) ? intval($this->seqs[$seqa]) : -1; 4755 $posb = isset($this->seqs[$seqb]) ? intval($this->seqs[$seqb]) : -1; 4756 4757 // return the relative position as the comparison value 4758 return $posa - $posb; 4257 uksort($headers, array($this, "compare_uids")); 4759 4258 } 4760 4259 -
trunk/roundcubemail/program/include/rcube_imap_cache.php
r5357 r5557 109 109 110 110 /** 111 * Return (sorted) messages index .111 * Return (sorted) messages index (UIDs). 112 112 * If index doesn't exist or is invalid, will be updated. 113 113 * … … 123 123 if (empty($this->icache[$mailbox])) 124 124 $this->icache[$mailbox] = array(); 125 125 console('cache::get_index'); 126 126 $sort_order = strtoupper($sort_order) == 'ASC' ? 'ASC' : 'DESC'; 127 127 … … 129 129 if (array_key_exists('index', $this->icache[$mailbox])) { 130 130 // The index was fetched from database already, but not validated yet 131 if (!array_key_exists(' result', $this->icache[$mailbox]['index'])) {131 if (!array_key_exists('object', $this->icache[$mailbox]['index'])) { 132 132 $index = $this->icache[$mailbox]['index']; 133 133 } 134 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 else140 return array_reverse($this->icache[$mailbox]['index']['result'], true);135 else if ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field) { 136 $result = $this->icache[$mailbox]['index']['object']; 137 if ($result->getParameters('ORDER') != $sort_order) { 138 $result->revert(); 139 } 140 return $result; 141 141 } 142 142 } … … 174 174 $is_valid = $this->validate($mailbox, $index, $exists); 175 175 } 176 176 console("valid:".$is_valid); 177 177 if ($is_valid) { 178 // build index, assign sequence IDs to unique IDs 179 $data = array_combine($index['seq'], $index['uid']); 178 $data = $index['object']; 180 179 // revert the order if needed 181 if ($index['sort_order'] != $sort_order) 182 $data = array_reverse($data, true); 180 if ($data->getParameters('ORDER') != $sort_order) { 181 $data->revert(); 182 } 183 183 } 184 184 } … … 202 202 203 203 // insert/update 204 $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, 205 $exists, $index['modseq']); 204 $this->add_index_row($mailbox, $sort_field, $data, $mbox_data, $exists, $index['modseq']); 206 205 } 207 206 208 207 $this->icache[$mailbox]['index'] = array( 209 ' result' => $data,208 'object' => $data, 210 209 'sort_field' => $sort_field, 211 'sort_order' => $sort_order,212 210 'modseq' => !empty($index['modseq']) ? $index['modseq'] : $mbox_data['HIGHESTMODSEQ'] 213 211 ); … … 234 232 // Seek in internal cache 235 233 if (array_key_exists('thread', $this->icache[$mailbox])) { 236 return array( 237 $this->icache[$mailbox]['thread']['tree'], 238 $this->icache[$mailbox]['thread']['depth'], 239 $this->icache[$mailbox]['thread']['children'], 240 ); 234 return $this->icache[$mailbox]['thread']['object']; 241 235 } 242 236 … … 251 245 } 252 246 253 $data = null;254 255 247 // Entry exist, check cache status 256 248 if (!empty($index)) { … … 270 262 if ($mbox_data['EXISTS']) { 271 263 // get all threads (default sort order) 272 list ($thread_tree, $msg_depth, $has_children) = $this->imap->fetch_threads($mailbox, true); 273 } 274 275 $index = array( 276 'tree' => !empty($thread_tree) ? $thread_tree : array(), 277 'depth' => !empty($msg_depth) ? $msg_depth : array(), 278 'children' => !empty($has_children) ? $has_children : array(), 279 ); 264 $threads = $this->imap->fetch_threads($mailbox, true); 265 } 266 else { 267 $threads = new rcube_imap_result($mailbox, ''); 268 } 269 270 $index['object'] = $threads; 280 271 281 272 // insert/update 282 $this->add_thread_row($mailbox, $ index, $mbox_data, $exists);273 $this->add_thread_row($mailbox, $threads, $mbox_data, $exists); 283 274 } 284 275 285 276 $this->icache[$mailbox]['thread'] = $index; 286 277 287 return array($index['tree'], $index['depth'], $index['children']);278 return $index['object']; 288 279 } 289 280 … … 293 284 * 294 285 * @param string $mailbox Folder name 295 * @param array $msgs Message sequence numbers 296 * @param bool $is_uid True if $msgs contains message UIDs 286 * @param array $msgs Message UIDs 297 287 * 298 288 * @return array The list of messages (rcube_mail_header) indexed by UID 299 289 */ 300 function get_messages($mailbox, $msgs = array() , $is_uid = true)290 function get_messages($mailbox, $msgs = array()) 301 291 { 302 292 if (empty($msgs)) { 303 293 return array(); 304 }305 306 // @TODO: it would be nice if we could work with UIDs only307 // then index would be not needed. For now we need it to308 // map id to uid here and to update message id for cached message309 310 // Convert IDs to UIDs311 $index = $this->get_index($mailbox, 'ANY');312 if (!$is_uid) {313 foreach ($msgs as $idx => $msgid)314 if ($uid = $index[$msgid])315 $msgs[$idx] = $uid;316 294 } 317 295 … … 335 313 $result[$uid]->body = null; 336 314 337 // update message ID according to index data338 if (!empty($index) && ($id = array_search($uid, $index)))339 $result[$uid]->id = $id;340 341 315 if (!empty($result[$uid])) { 342 316 unset($msgs[$uid]); … … 346 320 // Fetch not found messages from IMAP server 347 321 if (!empty($msgs)) { 348 $messages = $this->imap->fetch_headers($mailbox, array_keys($msgs), true, true);322 $messages = $this->imap->fetch_headers($mailbox, array_keys($msgs), false, true); 349 323 350 324 // Insert to DB and add to result list … … 392 366 $message = $this->build_message($sql_arr); 393 367 $found = true; 394 395 // update message ID according to index data396 $index = $this->get_index($mailbox, 'ANY');397 if (!empty($index) && ($id = array_search($uid, $index)))398 $message->id = $id;399 368 } 400 369 … … 618 587 619 588 /** 620 * @param string $mailbox Folder name621 * @param int $id Message (sequence) ID622 *623 * @return int Message UID624 */625 function id2uid($mailbox, $id)626 {627 if (!empty($this->icache['pending_index_update']))628 return null;629 630 // get index if it exists631 $index = $this->get_index($mailbox, 'ANY', null, true);632 633 return $index[$id];634 }635 636 637 /**638 * @param string $mailbox Folder name639 * @param int $uid Message UID640 *641 * @return int Message (sequence) ID642 */643 function uid2id($mailbox, $uid)644 {645 if (!empty($this->icache['pending_index_update']))646 return null;647 648 // get index if it exists649 $index = $this->get_index($mailbox, 'ANY', null, true);650 651 return array_search($uid, (array)$index);652 }653 654 /**655 589 * Fetches index data from database 656 590 */ 657 591 private function get_index_row($mailbox) 658 592 { 593 console('cache::get_index_row'); 659 594 // Get index from DB 660 595 $sql_result = $this->db->query( … … 666 601 667 602 if ($sql_arr = $this->db->fetch_assoc($sql_result)) { 668 $data = explode('@', $sql_arr['data']); 603 $data = explode('@', $sql_arr['data']); 604 $index = @unserialize($data[0]); 605 unset($data[0]); 606 607 if (empty($index)) { 608 $index = new rcube_result_index($mailbox); 609 } 669 610 670 611 return array( 671 612 'valid' => $sql_arr['valid'], 672 'seq' => explode(',', $data[0]), 673 'uid' => explode(',', $data[1]), 674 'sort_field' => $data[2], 675 'sort_order' => $data[3], 676 'deleted' => $data[4], 677 'validity' => $data[5], 678 'uidnext' => $data[6], 679 'modseq' => $data[7], 613 'object' => $index, 614 'sort_field' => $data[1], 615 'deleted' => $data[2], 616 'validity' => $data[3], 617 'uidnext' => $data[4], 618 'modseq' => $data[5], 680 619 ); 681 620 } … … 699 638 700 639 if ($sql_arr = $this->db->fetch_assoc($sql_result)) { 701 $data = explode('@', $sql_arr['data']); 702 703 // Uncompress data, see add_thread_row() 704 // $data[0] = str_replace(array('*', '^', '#'), array(';a:0:{}', 'i:', ';a:1:'), $data[0]); 705 $data[0] = unserialize($data[0]); 706 707 // build 'depth' and 'children' arrays 708 $depth = $children = array(); 709 $this->build_thread_data($data[0], $depth, $children); 640 $data = explode('@', $sql_arr['data']); 641 $thread = @unserialize($data[0]); 642 unset($data[0]); 643 644 if (empty($thread)) { 645 $thread = new rcube_imap_result($mailbox); 646 } 710 647 711 648 return array( 712 'tree' => $data[0], 713 'depth' => $depth, 714 'children' => $children, 649 'object' => $thread, 715 650 'deleted' => $data[1], 716 651 'validity' => $data[2], … … 726 661 * Saves index data into database 727 662 */ 728 private function add_index_row($mailbox, $sort_field, $sort_order,729 $data = array(), $mbox_data = array(), $exists = false, $modseq = null)663 private function add_index_row($mailbox, $sort_field, 664 $data, $mbox_data = array(), $exists = false, $modseq = null) 730 665 { 731 666 $data = array( 732 implode(',', array_keys($data)), 733 implode(',', array_values($data)), 667 serialize($data), 734 668 $sort_field, 735 $sort_order,736 669 (int) $this->skip_deleted, 737 670 (int) $mbox_data['UIDVALIDITY'], … … 760 693 * Saves thread data into database 761 694 */ 762 private function add_thread_row($mailbox, $data = array(), $mbox_data = array(), $exists = false) 763 { 764 $tree = serialize($data['tree']); 765 // This significantly reduces data length 766 // $tree = str_replace(array(';a:0:{}', 'i:', ';a:1:'), array('*', '^', '#'), $tree); 767 695 private function add_thread_row($mailbox, $data, $mbox_data = array(), $exists = false) 696 { 768 697 $data = array( 769 $tree,698 serialize($data), 770 699 (int) $this->skip_deleted, 771 700 (int) $mbox_data['UIDVALIDITY'], … … 795 724 private function validate($mailbox, $index, &$exists = true) 796 725 { 797 $is_thread = isset($index['tree']); 726 $object = $index['object']; 727 $is_thread = is_a($object, 'rcube_result_thread'); 798 728 799 729 // Get mailbox data (UIDVALIDITY, counters, etc.) for status check … … 815 745 // Folder is empty but cache isn't 816 746 if (empty($mbox_data['EXISTS'])) { 817 if (! empty($index['seq']) || !empty($index['tree'])) {747 if (!$object->isEmpty()) { 818 748 $this->clear($mailbox); 819 749 $exists = false; … … 822 752 } 823 753 // Folder is not empty but cache is 824 else if ( empty($index['seq']) && empty($index['tree'])) {754 else if ($object->isEmpty()) { 825 755 unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']); 826 756 return false; … … 829 759 // Validation flag 830 760 if (!$is_thread && empty($index['valid'])) { 831 unset($this->icache[$mailbox][ $is_thread ? 'thread' :'index']);761 unset($this->icache[$mailbox]['index']); 832 762 return false; 833 763 } … … 854 784 if ($is_thread) { 855 785 // check messages number... 856 if (!$this->skip_deleted && $mbox_data['EXISTS'] != @max(array_keys($index['depth']))) {786 if (!$this->skip_deleted && $mbox_data['EXISTS'] != $object->countMessages()) { 857 787 return false; 858 788 } … … 863 793 if (!empty($this->skip_deleted)) { 864 794 // compare counts if available 865 if ($mbox_data['COUNT_UNDELETED'] != null 866 && $mbox_data['COUNT_UNDELETED'] != count($index['uid'])) { 795 if (!empty($mbox_data['UNDELETED']) 796 && $mbox_data['UNDELETED']->count() != $object->count() 797 ) { 867 798 return false; 868 799 } 869 800 // compare UID sets 870 if ( $mbox_data['ALL_UNDELETED'] != null) {871 $uids_new = rcube_imap_generic::uncompressMessageSet($mbox_data['ALL_UNDELETED']);872 $uids_old = $ index['uid'];801 if (!empty($mbox_data['UNDELETED'])) { 802 $uids_new = $mbox_data['UNDELETED']->get(); 803 $uids_old = $object->get(); 873 804 874 805 if (count($uids_new) != count($uids_old)) { … … 885 816 // get all undeleted messages excluding cached UIDs 886 817 $ids = $this->imap->search_once($mailbox, 'ALL UNDELETED NOT UID '. 887 rcube_imap_generic::compressMessageSet($ index['uid']));888 889 if (! empty($ids)) {818 rcube_imap_generic::compressMessageSet($object->get())); 819 820 if (!$ids->isEmpty()) { 890 821 return false; 891 822 } … … 894 825 else { 895 826 // check messages number... 896 if ($mbox_data['EXISTS'] != max($index['seq'])) {827 if ($mbox_data['EXISTS'] != $object->count()) { 897 828 return false; 898 829 } 899 830 // ... and max UID 900 if ( max($index['uid']) != $this->imap->id2uid($mbox_data['EXISTS'], $mailbox, true)) {831 if ($object->max() != $this->imap->id2uid($mbox_data['EXISTS'], $mailbox, true)) { 901 832 return false; 902 833 } … … 1061 992 1062 993 $sort_field = $index['sort_field']; 1063 $sort_order = $index[' sort_order'];994 $sort_order = $index['object']->getParameters('ORDER'); 1064 995 $exists = true; 1065 996 … … 1070 1001 } 1071 1002 else { 1072 $data = array_combine($index['seq'], $index['uid']);1003 $data = $index['object']; 1073 1004 } 1074 1005 1075 1006 // update index and/or HIGHESTMODSEQ value 1076 $this->add_index_row($mailbox, $sort_field, $ sort_order, $data, $mbox_data, $exists);1007 $this->add_index_row($mailbox, $sort_field, $data, $mbox_data, $exists); 1077 1008 1078 1009 // update internal cache for get_index() 1079 $this->icache[$mailbox]['index'][' result'] = $data;1010 $this->icache[$mailbox]['index']['object'] = $data; 1080 1011 } 1081 1012 … … 1104 1035 1105 1036 /** 1106 * Creates 'depth' and 'children' arrays from stored thread 'tree' data.1107 */1108 private function build_thread_data($data, &$depth, &$children, $level = 0)1109 {1110 foreach ((array)$data as $key => $val) {1111 $children[$key] = !empty($val);1112 $depth[$key] = $level;1113 if (!empty($val))1114 $this->build_thread_data($val, $depth, $children, $level + 1);1115 }1116 }1117 1118 1119 /**1120 1037 * Saves message stored in internal cache 1121 1038 */ … … 1171 1088 private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array()) 1172 1089 { 1173 $data = array();1174 1175 1090 if (empty($mbox_data)) { 1176 1091 $mbox_data = $this->imap->mailbox_data($mailbox); 1177 1092 } 1178 1093 1179 // Prevent infinite loop.1180 // It happens when rcube_imap::message_index_direct() is called.1181 // There id2uid() is called which will again call get_index() and so on.1182 if (!$sort_field && !$this->skip_deleted)1183 $this->icache['pending_index_update'] = true;1184 1185 1094 if ($mbox_data['EXISTS']) { 1186 1095 // fetch sorted sequence numbers 1187 $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); 1188 // fetch UIDs 1189 if (!empty($data_seq)) { 1190 // Seek in internal cache 1191 if (array_key_exists('index', (array)$this->icache[$mailbox]) 1192 && array_key_exists('result', (array)$this->icache[$mailbox]['index']) 1193 ) 1194 $data_uid = $this->icache[$mailbox]['index']['result']; 1195 else 1196 $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq); 1197 1198 // build index 1199 if (!empty($data_uid)) { 1200 foreach ($data_seq as $seq) 1201 if ($uid = $data_uid[$seq]) 1202 $data[$seq] = $uid; 1203 } 1204 } 1205 } 1206 1207 // Reset internal flags 1208 $this->icache['pending_index_update'] = false; 1209 1210 return $data; 1096 $index = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); 1097 } 1098 else { 1099 $index = new rcube_result_index($mailbox, '* SORT'); 1100 } 1101 1102 return $index; 1211 1103 } 1212 1104 } -
trunk/roundcubemail/program/include/rcube_imap_generic.php
r5400 r5557 26 26 27 27 */ 28 29 28 30 29 /** … … 1219 1218 // Invoke SEARCH as a fallback 1220 1219 $index = $this->search($mailbox, 'ALL UNSEEN', false, array('COUNT')); 1221 if ( is_array($index)) {1222 return (int) $index['COUNT'];1220 if (!$index->isError()) { 1221 return $index->count(); 1223 1222 } 1224 1223 … … 1294 1293 } 1295 1294 1296 function sort($mailbox, $field, $add='', $is_uid=FALSE, $encoding = 'US-ASCII') 1297 { 1295 /** 1296 * Executes SORT command 1297 * 1298 * @param string $mailbox Mailbox name 1299 * @param string $field Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO) 1300 * @param string $add Searching criteria 1301 * @param bool $return_uid Enables UID SORT usage 1302 * @param string $encoding Character set 1303 * 1304 * @return rcube_result_index Response data 1305 */ 1306 function sort($mailbox, $field, $add='', $return_uid=false, $encoding = 'US-ASCII') 1307 { 1308 require_once dirname(__FILE__) . '/rcube_result_index.php'; 1309 1298 1310 $field = strtoupper($field); 1299 1311 if ($field == 'INTERNALDATE') { … … 1305 1317 1306 1318 if (!$fields[$field]) { 1307 return false;1319 return new rcube_result_index($mailbox); 1308 1320 } 1309 1321 1310 1322 if (!$this->select($mailbox)) { 1311 return false;1323 return new rcube_result_index($mailbox); 1312 1324 } 1313 1325 … … 1316 1328 $add = $this->compressMessageSet($add); 1317 1329 1318 list($code, $response) = $this->execute($ is_uid ? 'UID SORT' : 'SORT',1330 list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT', 1319 1331 array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : ''))); 1320 1332 1321 if ($code == self::ERROR_OK) { 1322 // remove prefix and unilateral untagged server responses 1323 $response = substr($response, stripos($response, '* SORT') + 7); 1324 if ($pos = strpos($response, '*')) { 1325 $response = substr($response, 0, $pos); 1326 } 1327 return preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY); 1328 } 1329 1330 return false; 1331 } 1332 1333 function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) 1333 if ($code != self::ERROR_OK) { 1334 $response = null; 1335 } 1336 1337 return new rcube_result_index($mailbox, $response); 1338 } 1339 1340 /** 1341 * Simulates SORT command by using FETCH and sorting. 1342 * 1343 * @param string $mailbox Mailbox name 1344 * @param string|array $message_set Searching criteria (list of messages to return) 1345 * @param string $index_field Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO) 1346 * @param bool $skip_deleted Makes that DELETED messages will be skipped 1347 * @param bool $uidfetch Enables UID FETCH usage 1348 * @param bool $return_uid Enables returning UIDs instead of IDs 1349 * 1350 * @return rcube_result_index Response data 1351 */ 1352 function index($mailbox, $message_set, $index_field='', $skip_deleted=true, 1353 $uidfetch=false, $return_uid=false) 1354 { 1355 require_once dirname(__FILE__) . '/rcube_result_index.php'; 1356 1357 $msg_index = $this->fetchHeaderIndex($mailbox, $message_set, 1358 $index_field, $skip_deleted, $uidfetch, $return_uid); 1359 1360 if (!empty($msg_index)) { 1361 asort($msg_index); // ASC 1362 $msg_index = array_keys($msg_index); 1363 $msg_index = '* SEARCH ' . implode(' ', $msg_index); 1364 } 1365 else { 1366 $msg_index = is_array($msg_index) ? '* SEARCH' : null; 1367 } 1368 1369 return new rcube_result_index($mailbox, $msg_index); 1370 } 1371 1372 function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, 1373 $uidfetch=false, $return_uid=false) 1334 1374 { 1335 1375 if (is_array($message_set)) { … … 1371 1411 1372 1412 // build FETCH command string 1373 $key = $this->nextTag(); 1374 $cmd = $uidfetch ? 'UID FETCH' : 'FETCH'; 1375 $deleted = $skip_deleted ? ' FLAGS' : ''; 1376 1377 if ($mode == 1 && $index_field == 'DATE') 1378 $request = " $cmd $message_set (INTERNALDATE BODY.PEEK[HEADER.FIELDS (DATE)]$deleted)"; 1379 else if ($mode == 1) 1380 $request = " $cmd $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)]$deleted)"; 1413 $key = $this->nextTag(); 1414 $cmd = $uidfetch ? 'UID FETCH' : 'FETCH'; 1415 $fields = array(); 1416 1417 if ($return_uid) 1418 $fields[] = 'UID'; 1419 if ($skip_deleted) 1420 $fields[] = 'FLAGS'; 1421 1422 if ($mode == 1) { 1423 if ($index_field == 'DATE') 1424 $fields[] = 'INTERNALDATE'; 1425 $fields[] = "BODY.PEEK[HEADER.FIELDS ($index_field)]"; 1426 } 1381 1427 else if ($mode == 2) { 1382 1428 if ($index_field == 'SIZE') 1383 $request = " $cmd $message_set (RFC822.SIZE$deleted)"; 1384 else 1385 $request = " $cmd $message_set ($index_field$deleted)"; 1386 } else if ($mode == 3) 1387 $request = " $cmd $message_set (FLAGS)"; 1388 else // 4 1389 $request = " $cmd $message_set (INTERNALDATE$deleted)"; 1390 1391 $request = $key . $request; 1429 $fields[] = 'RFC822.SIZE'; 1430 else if (!$return_uid || $index_field != 'UID') 1431 $fields[] = $index_field; 1432 } 1433 else if ($mode == 3 && !$skip_deleted) 1434 $fields[] = 'FLAGS'; 1435 else if ($mode == 4) 1436 $fields[] = 'INTERNALDATE'; 1437 1438 $request = "$key $cmd $message_set (" . implode(' ', $fields) . ")"; 1392 1439 1393 1440 if (!$this->putLine($request)) { … … 1406 1453 $flags = NULL; 1407 1454 1455 if ($return_uid) { 1456 if (preg_match('/UID ([0-9]+)/', $line, $matches)) 1457 $id = (int) $matches[1]; 1458 else 1459 continue; 1460 } 1408 1461 if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { 1409 1462 $flags = explode(' ', strtoupper($matches[1])); … … 1535 1588 { 1536 1589 if ($uid > 0) { 1537 $id_a = $this->search($mailbox, "UID $uid"); 1538 if (is_array($id_a) && count($id_a) == 1) { 1539 return (int) $id_a[0]; 1590 $index = $this->search($mailbox, "UID $uid"); 1591 1592 if ($index->count() == 1) { 1593 $arr = $index->get(); 1594 return (int) $arr[0]; 1540 1595 } 1541 1596 } … … 1561 1616 } 1562 1617 1563 list($code, $response) = $this->execute('FETCH', array($id, '(UID)')); 1564 1565 if ($code == self::ERROR_OK && preg_match("/^\* $id FETCH \(UID (.*)\)/i", $response, $m)) { 1566 return (int) $m[1]; 1618 $index = $this->search($mailbox, $id, true); 1619 1620 if ($index->count() == 1) { 1621 $arr = $index->get(); 1622 return (int) $arr[0]; 1567 1623 } 1568 1624 1569 1625 return null; 1570 }1571 1572 function fetchUIDs($mailbox, $message_set=null)1573 {1574 if (empty($message_set))1575 $message_set = '1:*';1576 1577 return $this->fetchHeaderIndex($mailbox, $message_set, 'UID', false);1578 1626 } 1579 1627 … … 1971 2019 } 1972 2020 1973 // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about 1974 // 7 times instead :-) See comments on http://uk2.php.net/references and this article: 1975 // http://derickrethans.nl/files/phparch-php-variables-article.pdf 1976 private function parseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) 1977 { 1978 $node = array(); 1979 if ($str[$begin] != '(') { 1980 $stop = $begin + strspn($str, '1234567890', $begin, $end - $begin); 1981 $msg = substr($str, $begin, $stop - $begin); 1982 if ($msg == 0) 1983 return $node; 1984 if (is_null($root)) 1985 $root = $msg; 1986 $depthmap[$msg] = $depth; 1987 $haschildren[$msg] = false; 1988 if (!is_null($parent)) 1989 $haschildren[$parent] = true; 1990 if ($stop + 1 < $end) 1991 $node[$msg] = $this->parseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren); 1992 else 1993 $node[$msg] = array(); 1994 } else { 1995 $off = $begin; 1996 while ($off < $end) { 1997 $start = $off; 1998 $off++; 1999 $n = 1; 2000 while ($n > 0) { 2001 $p = strpos($str, ')', $off); 2002 if ($p === false) { 2003 error_log("Mismatched brackets parsing IMAP THREAD response:"); 2004 error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20)); 2005 error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10)))); 2006 return $node; 2007 } 2008 $p1 = strpos($str, '(', $off); 2009 if ($p1 !== false && $p1 < $p) { 2010 $off = $p1 + 1; 2011 $n++; 2012 } else { 2013 $off = $p + 1; 2014 $n--; 2015 } 2016 } 2017 $node += $this->parseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren); 2018 } 2019 } 2020 2021 return $node; 2022 } 2023 2024 function thread($mailbox, $algorithm='REFERENCES', $criteria='', $encoding='US-ASCII') 2025 { 2021 /** 2022 * Executes THREAD command 2023 * 2024 * @param string $mailbox Mailbox name 2025 * @param string $algorithm Threading algorithm (ORDEREDSUBJECT, REFERENCES, REFS) 2026 * @param string $criteria Searching criteria 2027 * @param bool $return_uid Enables UIDs in result instead of sequence numbers 2028 * @param string $encoding Character set 2029 * 2030 * @return rcube_result_thread Thread data 2031 */ 2032 function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII') 2033 { 2034 require_once dirname(__FILE__) . '/rcube_result_thread.php'; 2035 2026 2036 $old_sel = $this->selected; 2027 2037 2028 2038 if (!$this->select($mailbox)) { 2029 return false;2039 return new rcube_result_thread($mailbox); 2030 2040 } 2031 2041 2032 2042 // return empty result when folder is empty and we're just after SELECT 2033 2043 if ($old_sel != $mailbox && !$this->data['EXISTS']) { 2034 return array(array(), array(), array());2044 return new rcube_result_thread($mailbox); 2035 2045 } 2036 2046 … … 2040 2050 $data = ''; 2041 2051 2042 list($code, $response) = $this->execute('THREAD', array( 2043 $algorithm, $encoding, $criteria)); 2044 2045 if ($code == self::ERROR_OK) { 2046 // remove prefix... 2047 $response = substr($response, stripos($response, '* THREAD') + 9); 2048 // ...unilateral untagged server responses 2049 if ($pos = strpos($response, '*')) { 2050 $response = substr($response, 0, $pos); 2051 } 2052 2053 $response = str_replace("\r\n", '', $response); 2054 $depthmap = array(); 2055 $haschildren = array(); 2056 2057 $tree = $this->parseThread($response, 0, strlen($response), 2058 null, null, 0, $depthmap, $haschildren); 2059 2060 return array($tree, $depthmap, $haschildren); 2061 } 2062 2063 return false; 2052 list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD', 2053 array($algorithm, $encoding, $criteria)); 2054 2055 if ($code != self::ERROR_OK) { 2056 $response = null; 2057 } 2058 2059 return new rcube_result_thread($mailbox, $response); 2064 2060 } 2065 2061 … … 2072 2068 * @param array $items Return items (MIN, MAX, COUNT, ALL) 2073 2069 * 2074 * @return array Message identifiers or item-value hash2070 * @return rcube_result_index Result data 2075 2071 */ 2076 2072 function search($mailbox, $criteria, $return_uid=false, $items=array()) 2077 2073 { 2074 require_once dirname(__FILE__) . '/rcube_result_index.php'; 2075 2078 2076 $old_sel = $this->selected; 2079 2077 2080 2078 if (!$this->select($mailbox)) { 2081 return false;2079 return new rcube_result_index($mailbox); 2082 2080 } 2083 2081 2084 2082 // return empty result when folder is empty and we're just after SELECT 2085 2083 if ($old_sel != $mailbox && !$this->data['EXISTS']) { 2086 if (!empty($items)) 2087 return array_combine($items, array_fill(0, count($items), 0)); 2088 else 2089 return array(); 2084 return new rcube_result_index($mailbox, '* SEARCH'); 2085 } 2086 2087 // If ESEARCH is supported always use ALL 2088 // but not when items are specified or using simple id2uid search 2089 if (empty($items) && ((int) $criteria != $criteria)) { 2090 $items = array('ALL'); 2090 2091 } 2091 2092 … … 2098 2099 $params .= 'RETURN (' . implode(' ', $items) . ')'; 2099 2100 } 2101 2100 2102 if (!empty($criteria)) { 2101 2103 $modseq = stripos($criteria, 'MODSEQ') !== false; … … 2109 2111 array($params)); 2110 2112 2111 if ($code == self::ERROR_OK) { 2112 // remove prefix... 2113 $response = substr($response, stripos($response, 2114 $esearch ? '* ESEARCH' : '* SEARCH') + ($esearch ? 10 : 9)); 2115 // ...and unilateral untagged server responses 2116 if ($pos = strpos($response, '*')) { 2117 $response = rtrim(substr($response, 0, $pos)); 2118 } 2119 2120 // remove MODSEQ response 2121 if ($modseq) { 2122 if (preg_match('/\(MODSEQ ([0-9]+)\)$/', $response, $m)) { 2123 $response = substr($response, 0, -strlen($m[0])); 2124 } 2125 } 2126 2127 if ($esearch) { 2128 // Skip prefix: ... (TAG "A285") UID ... 2129 $this->tokenizeResponse($response, $return_uid ? 2 : 1); 2130 2131 $result = array(); 2132 for ($i=0; $i<count($items); $i++) { 2133 // If the SEARCH returns no matches, the server MUST NOT 2134 // include the item result option in the ESEARCH response 2135 if ($ret = $this->tokenizeResponse($response, 2)) { 2136 list ($name, $value) = $ret; 2137 $result[$name] = $value; 2138 } 2139 } 2140 2141 return $result; 2142 } 2143 else { 2144 $response = preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY); 2145 2146 if (!empty($items)) { 2147 $result = array(); 2148 if (in_array('COUNT', $items)) { 2149 $result['COUNT'] = count($response); 2150 } 2151 if (in_array('MIN', $items)) { 2152 $result['MIN'] = !empty($response) ? min($response) : 0; 2153 } 2154 if (in_array('MAX', $items)) { 2155 $result['MAX'] = !empty($response) ? max($response) : 0; 2156 } 2157 if (in_array('ALL', $items)) { 2158 $result['ALL'] = $this->compressMessageSet($response, true); 2159 } 2160 2161 return $result; 2162 } 2163 else { 2164 return $response; 2165 } 2166 } 2167 } 2168 2169 return false; 2113 if ($code != self::ERROR_OK) { 2114 $response = null; 2115 } 2116 2117 return new rcube_result_index($mailbox, $response); 2170 2118 } 2171 2119 -
trunk/roundcubemail/program/steps/mail/func.inc
r5521 r5557 94 94 $_SESSION['search_request'] = $search_request; 95 95 $OUTPUT->set_env('search_request', $search_request); 96 }96 } 97 97 98 98 $search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT); -
trunk/roundcubemail/program/steps/mail/pagenav.inc
r4873 r5557 20 20 */ 21 21 22 $uid = get_input_value('_uid', RCUBE_INPUT_GET); 22 $uid = get_input_value('_uid', RCUBE_INPUT_GET); 23 $index = $IMAP->message_index(null, $_SESSION['sort_col'], $_SESSION['sort_order']); 24 $cnt = $index->countMessages(); 23 25 24 // Select mailbox first, for better performance 25 $mbox_name = $IMAP->get_mailbox_name(); 26 $IMAP->select_mailbox($mbox_name); 27 28 // Get messages count (only messages, no threads here) 29 $cnt = $IMAP->messagecount(NULL, 'ALL'); 30 31 if ($_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC' 32 && empty($_REQUEST['_search']) && !$CONFIG['skip_deleted'] && !$IMAP->threading 33 ) { 34 // this assumes that we are sorted by date_DESC 35 $seq = $IMAP->get_id($uid); 36 $index = $cnt - $seq; 37 38 $prev = $IMAP->get_uid($seq + 1); 39 $first = $IMAP->get_uid($cnt); 40 $next = $IMAP->get_uid($seq - 1); 41 $last = $IMAP->get_uid(1); 42 } 43 else { 44 // Only if we use custom sorting 45 $a_msg_index = $IMAP->message_index(NULL, $_SESSION['sort_col'], $_SESSION['sort_order']); 46 47 $index = array_search($IMAP->get_id($uid), $a_msg_index); 48 49 $count = count($a_msg_index); 50 $prev = isset($a_msg_index[$index-1]) ? $IMAP->get_uid($a_msg_index[$index-1]) : -1; 51 $first = $count > 1 ? $IMAP->get_uid($a_msg_index[0]) : -1; 52 $next = isset($a_msg_index[$index+1]) ? $IMAP->get_uid($a_msg_index[$index+1]) : -1; 53 $last = $count > 1 ? $IMAP->get_uid($a_msg_index[$count-1]) : -1; 26 if ($cnt && ($pos = $index->exists($uid, true)) !== false) { 27 $prev = $pos ? $index->getElement($pos-1) : 0; 28 $first = $pos ? $index->getElement('FIRST') : 0; 29 $next = $pos < $cnt-1 ? $index->getElement($pos+1) : 0; 30 $last = $pos < $cnt-1 ? $index->getElement('LAST') : 0; 54 31 } 55 32 56 33 // Set UIDs and activate navigation buttons 57 if ($prev > 0) {34 if ($prev) { 58 35 $OUTPUT->set_env('prev_uid', $prev); 59 36 $OUTPUT->command('enable_command', 'previousmessage', 'firstmessage', true); 60 37 } 61 if ($next > 0) {38 if ($next) { 62 39 $OUTPUT->set_env('next_uid', $next); 63 40 $OUTPUT->command('enable_command', 'nextmessage', 'lastmessage', true); 64 41 } 65 if ($first > 0)42 if ($first) 66 43 $OUTPUT->set_env('first_uid', $first); 67 if ($last > 0)44 if ($last) 68 45 $OUTPUT->set_env('last_uid', $last); 69 46 … … 74 51 $OUTPUT->command('set_rowcount', rcube_label(array( 75 52 'name' => 'messagenrof', 76 'vars' => array('nr' => $ index+1, 'count' => $cnt)53 'vars' => array('nr' => $pos+1, 'count' => $cnt) 77 54 ))); 78 55 -
trunk/roundcubemail/program/steps/mail/search.inc
r5520 r5557 110 110 $IMAP->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']); 111 111 112 // Get the headers113 $result_h = $IMAP->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']);114 $count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL');115 116 112 // save search results in session 117 113 if (!is_array($_SESSION['search'])) … … 123 119 } 124 120 $_SESSION['search_request'] = $search_request; 121 122 123 // Get the headers 124 $result_h = $IMAP->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']); 125 $count = $IMAP->messagecount($mbox, $IMAP->threading ? 'THREADS' : 'ALL'); 126 125 127 126 128 // Make sure we got the headers -
trunk/roundcubemail/program/steps/mail/sendmail.inc
r5521 r5557 702 702 // delete previous saved draft 703 703 // @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH 704 $ a_deleteid= $IMAP->search_once($CONFIG['drafts_mbox'],705 'HEADER Message-ID '.$olddraftmessageid , true);706 707 if ( !empty($a_deleteid)) {708 $deleted = $IMAP->delete_message($ a_deleteid, $CONFIG['drafts_mbox']);704 $delete_idx = $IMAP->search_once($CONFIG['drafts_mbox'], 705 'HEADER Message-ID '.$olddraftmessageid); 706 707 if ($del_uid = $delete_idx->getElement('FIRST')) { 708 $deleted = $IMAP->delete_message($del_uid, $CONFIG['drafts_mbox']); 709 709 710 710 // raise error if deletion of old draft failed … … 727 727 // remember new draft-uid ($saved could be an UID or TRUE here) 728 728 if (is_bool($saved)) { 729 $draft uids = $IMAP->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid, true);730 $saved = $draft uids[0];729 $draft_idx = $IMAP->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid); 730 $saved = $draft_idx->getElement('FIRST'); 731 731 } 732 732 $COMPOSE['param']['draft_uid'] = $saved;
Note: See TracChangeset
for help on using the changeset viewer.
