1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "seq-range-array.h"
6 #include "mail-search.h"
7 #include "mailbox-search-result-private.h"
8 #include "index-storage.h"
9 #include "index-search-result.h"
10 
11 static void
search_result_range_remove(struct mail_search_result * result,const ARRAY_TYPE (seq_range)* changed_uids_arr,unsigned int * idx,uint32_t * next_uid,uint32_t last_uid)12 search_result_range_remove(struct mail_search_result *result,
13 			   const ARRAY_TYPE(seq_range) *changed_uids_arr,
14 			   unsigned int *idx,
15 			   uint32_t *next_uid, uint32_t last_uid)
16 {
17 	const struct seq_range *uids;
18 	unsigned int i, count;
19 	uint32_t uid;
20 
21 	/* remove full seq_ranges */
22 	uid = *next_uid;
23 	uids = array_get(changed_uids_arr, &count);
24 	for (i = *idx; uids[i].seq2 < last_uid;) {
25 		i_assert(uids[i].seq1 <= uid);
26 		for (; uid <= uids[i].seq2; uid++)
27 			mailbox_search_result_remove(result, uid);
28 		i++;
29 		i_assert(i < count);
30 		uid = uids[i].seq1;
31 	}
32 
33 	/* remove the last seq_range */
34 	i_assert(uids[i].seq1 <= uid && uids[i].seq2 >= last_uid);
35 	for (; uid <= last_uid; uid++)
36 		mailbox_search_result_remove(result, uid);
37 
38 	if (uid > uids[i].seq2) {
39 		/* finished this range */
40 		if (++i < count)
41 			uid = uids[i].seq1;
42 		else {
43 			/* this was the last searched message */
44 			uid = 0;
45 		}
46 	}
47 
48 	*next_uid = uid;
49 	*idx = i;
50 }
51 
52 static int
search_result_update_search(struct mail_search_result * result,const ARRAY_TYPE (seq_range)* changed_uids_arr)53 search_result_update_search(struct mail_search_result *result,
54 			    const ARRAY_TYPE(seq_range) *changed_uids_arr)
55 {
56 	struct mailbox_transaction_context *t;
57 	struct mail_search_context *search_ctx;
58 	struct mail *mail;
59 	const struct seq_range *changed_uids;
60 	unsigned int changed_count, changed_idx;
61 	uint32_t next_uid;
62 	int ret;
63 
64 	changed_uids = array_get(changed_uids_arr, &changed_count);
65 	i_assert(changed_count > 0);
66 	next_uid = changed_uids[0].seq1;
67 	changed_idx = 0;
68 
69 	mail_search_args_init(result->search_args, result->box, FALSE, NULL);
70 
71 	t = mailbox_transaction_begin(result->box, 0, __func__);
72 	search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL);
73 	/* tell search that we're updating an existing search result,
74 	   so it can do some optimizations based on it */
75 	search_ctx->update_result = result;
76 
77 	while (mailbox_search_next(search_ctx, &mail)) {
78 		i_assert(next_uid != 0);
79 
80 		if (next_uid != mail->uid) {
81 			/* some messages in changed_uids didn't match.
82 			   make sure they don't exist in the search result. */
83 			search_result_range_remove(result, changed_uids_arr,
84 						   &changed_idx, &next_uid,
85 						   mail->uid-1);
86 			i_assert(next_uid == mail->uid);
87 		}
88 		if (changed_uids[changed_idx].seq2 > next_uid) {
89 			next_uid++;
90 		} else if (++changed_idx < changed_count) {
91 			next_uid = changed_uids[changed_idx].seq1;
92 		} else {
93 			/* this was the last searched message */
94 			next_uid = 0;
95 		}
96 		/* match - make sure it exists in search result */
97 		mailbox_search_result_add(result, mail->uid);
98 	}
99 	mail_search_args_deinit(result->search_args);
100 	ret = mailbox_search_deinit(&search_ctx);
101 
102 	if (next_uid != 0 && ret == 0) {
103 		/* last message(s) didn't match. make sure they don't exist
104 		   in the search result. */
105 		search_result_range_remove(result, changed_uids_arr,
106 					   &changed_idx, &next_uid,
107 					   changed_uids[changed_count-1].seq2);
108 	}
109 
110 	if (mailbox_transaction_commit(&t) < 0)
111 		ret = -1;
112 	return ret;
113 }
114 
index_search_result_update_flags(struct mail_search_result * result,const ARRAY_TYPE (seq_range)* uids)115 int index_search_result_update_flags(struct mail_search_result *result,
116 				     const ARRAY_TYPE(seq_range) *uids)
117 {
118 	struct mail_search_arg search_arg;
119 	int ret;
120 
121 	if (array_count(uids) == 0)
122 		return 0;
123 
124 	/* add a temporary search parameter to limit the search only to
125 	   the changed messages */
126 	i_zero(&search_arg);
127 	search_arg.type = SEARCH_UIDSET;
128 	search_arg.value.seqset = *uids;
129 	search_arg.next = result->search_args->args;
130 	result->search_args->args = &search_arg;
131 	ret = search_result_update_search(result, uids);
132 	i_assert(result->search_args->args == &search_arg);
133 	result->search_args->args = search_arg.next;
134 	return ret;
135 }
136 
index_search_result_update_appends(struct mail_search_result * result,unsigned int old_messages_count)137 int index_search_result_update_appends(struct mail_search_result *result,
138 				       unsigned int old_messages_count)
139 {
140 	struct mailbox_transaction_context *t;
141 	struct mail_search_context *search_ctx;
142 	struct mail *mail;
143 	struct mail_search_arg search_arg;
144 	uint32_t message_count;
145 	int ret;
146 
147 	message_count = mail_index_view_get_messages_count(result->box->view);
148 	if (old_messages_count == message_count) {
149 		/* no new messages */
150 		return 0;
151 	}
152 
153 	/* add a temporary search parameter to limit the search only to
154 	   the new messages */
155 	i_zero(&search_arg);
156 	search_arg.type = SEARCH_SEQSET;
157 	t_array_init(&search_arg.value.seqset, 1);
158 	seq_range_array_add_range(&search_arg.value.seqset,
159 				  old_messages_count + 1, message_count);
160 	search_arg.next = result->search_args->args;
161 	result->search_args->args = &search_arg;
162 
163 	/* add all messages matching the search to search result */
164 	t = mailbox_transaction_begin(result->box, 0, __func__);
165 	search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL);
166 
167 	while (mailbox_search_next(search_ctx, &mail))
168 		mailbox_search_result_add(result, mail->uid);
169 
170 	ret = mailbox_search_deinit(&search_ctx);
171 	if (mailbox_transaction_commit(&t) < 0)
172 		ret = -1;
173 
174 	i_assert(result->search_args->args == &search_arg);
175 	result->search_args->args = search_arg.next;
176 	return ret;
177 }
178 
index_search_results_update_expunges(struct mailbox * box,const ARRAY_TYPE (seq_range)* expunges)179 void index_search_results_update_expunges(struct mailbox *box,
180 					  const ARRAY_TYPE(seq_range) *expunges)
181 {
182 	const struct seq_range *seqs;
183 	uint32_t seq, uid;
184 
185 	if (array_count(&box->search_results) == 0)
186 		return;
187 
188 	array_foreach(expunges, seqs) {
189 		for (seq = seqs->seq1; seq <= seqs->seq2; seq++) {
190 			mail_index_lookup_uid(box->view, seq, &uid);
191 			mailbox_search_results_remove(box, uid);
192 		}
193 	}
194 }
195