1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "imap-common.h"
4 #include "mail-storage.h"
5 #include "mail-search-parser.h"
6 #include "mail-search-build.h"
7 #include "imap-search-args.h"
8 #include "imap-parser.h"
9 #include "imap-seqset.h"
10
11
12 struct search_build_data {
13 pool_t pool;
14 struct mailbox *box;
15 const char *error;
16 };
17
search_args_have_searchres(struct mail_search_arg * sargs)18 static bool search_args_have_searchres(struct mail_search_arg *sargs)
19 {
20 for (; sargs != NULL; sargs = sargs->next) {
21 switch (sargs->type) {
22 case SEARCH_UIDSET:
23 if (strcmp(sargs->value.str, "$") == 0)
24 return TRUE;
25 break;
26 case SEARCH_SUB:
27 case SEARCH_OR:
28 if (search_args_have_searchres(sargs->value.subargs))
29 return TRUE;
30 break;
31 default:
32 break;
33 }
34 }
35 return FALSE;
36 }
37
imap_search_saved_uidset_clear(struct client_command_context * cmd)38 static void imap_search_saved_uidset_clear(struct client_command_context *cmd)
39 {
40 if (array_is_created(&cmd->client->search_saved_uidset))
41 array_clear(&cmd->client->search_saved_uidset);
42 else
43 i_array_init(&cmd->client->search_saved_uidset, 128);
44 }
45
imap_search_args_build(struct client_command_context * cmd,const struct imap_arg * args,const char * charset,struct mail_search_args ** search_args_r)46 int imap_search_args_build(struct client_command_context *cmd,
47 const struct imap_arg *args, const char *charset,
48 struct mail_search_args **search_args_r)
49 {
50 struct mail_search_parser *parser;
51 struct mail_search_args *sargs;
52 const char *client_error;
53 int ret;
54
55 if (IMAP_ARG_IS_EOL(args)) {
56 client_send_command_error(cmd, "Missing search parameters");
57 return -1;
58 }
59
60 parser = mail_search_parser_init_imap(args);
61 ret = mail_search_build(mail_search_register_get_imap(),
62 parser, &charset, &sargs, &client_error);
63 mail_search_parser_deinit(&parser);
64 if (ret < 0) {
65 if (charset == NULL) {
66 if (cmd->search_save_result)
67 imap_search_saved_uidset_clear(cmd);
68 client_send_tagline(cmd, t_strconcat(
69 "NO [BADCHARSET] ", client_error, NULL));
70 } else {
71 client_send_command_error(cmd, client_error);
72 }
73 return -1;
74 }
75
76 if (search_args_have_searchres(sargs->args)) {
77 if (client_handle_search_save_ambiguity(cmd))
78 return 0;
79 }
80
81 mail_search_args_init(sargs, cmd->client->mailbox, TRUE,
82 &cmd->client->search_saved_uidset);
83 if (cmd->search_save_result) {
84 /* clear the SAVE resultset only after potentially using $
85 in the search args themselves */
86 imap_search_saved_uidset_clear(cmd);
87 }
88 *search_args_r = sargs;
89 return 1;
90 }
91
92 static bool
msgset_is_valid(ARRAY_TYPE (seq_range)* seqset,uint32_t messages_count)93 msgset_is_valid(ARRAY_TYPE(seq_range) *seqset, uint32_t messages_count)
94 {
95 const struct seq_range *range;
96 unsigned int count;
97
98 /* when there are no messages, all messagesets are invalid.
99 if there's at least one message:
100 - * gives seq1 = seq2 = (uint32_t)-1
101 - n:* should work if n <= messages_count
102 - n:m or m should work if m <= messages_count
103 */
104 range = array_get(seqset, &count);
105 if (count == 0 || messages_count == 0)
106 return FALSE;
107
108 if (range[count-1].seq2 == (uint32_t)-1) {
109 if (range[count-1].seq1 > messages_count &&
110 range[count-1].seq1 != (uint32_t)-1)
111 return FALSE;
112 } else {
113 if (range[count-1].seq2 > messages_count)
114 return FALSE;
115 }
116 return TRUE;
117 }
118
imap_search_get_msgset_arg(struct client_command_context * cmd,const char * messageset,struct mail_search_args ** args_r,const char ** error_r)119 static int imap_search_get_msgset_arg(struct client_command_context *cmd,
120 const char *messageset,
121 struct mail_search_args **args_r,
122 const char **error_r)
123 {
124 struct mail_search_args *args;
125
126 args = mail_search_build_init();
127 args->args = p_new(args->pool, struct mail_search_arg, 1);
128 args->args->type = SEARCH_SEQSET;
129 p_array_init(&args->args->value.seqset, args->pool, 16);
130 if (imap_seq_set_parse(messageset, &args->args->value.seqset) < 0 ||
131 !msgset_is_valid(&args->args->value.seqset,
132 cmd->client->messages_count)) {
133 *error_r = "Invalid messageset";
134 mail_search_args_unref(&args);
135 return -1;
136 }
137 *args_r = args;
138 return 0;
139 }
140
141 static int
imap_search_get_uidset_arg(const char * uidset,struct mail_search_args ** args_r,const char ** error_r)142 imap_search_get_uidset_arg(const char *uidset, struct mail_search_args **args_r,
143 const char **error_r)
144 {
145 struct mail_search_args *args;
146
147 args = mail_search_build_init();
148 args->args = p_new(args->pool, struct mail_search_arg, 1);
149 args->args->type = SEARCH_UIDSET;
150 p_array_init(&args->args->value.seqset, args->pool, 16);
151 if (imap_seq_set_parse(uidset, &args->args->value.seqset) < 0) {
152 *error_r = "Invalid uidset";
153 mail_search_args_unref(&args);
154 return -1;
155 }
156
157 *args_r = args;
158 return 0;
159 }
160
imap_search_get_seqset(struct client_command_context * cmd,const char * set,bool uid,struct mail_search_args ** search_args_r)161 int imap_search_get_seqset(struct client_command_context *cmd,
162 const char *set, bool uid,
163 struct mail_search_args **search_args_r)
164 {
165 int ret;
166
167 ret = imap_search_get_anyset(cmd, set, uid, search_args_r);
168 if (ret > 0) {
169 mail_search_args_init(*search_args_r,
170 cmd->client->mailbox, TRUE,
171 &cmd->client->search_saved_uidset);
172 }
173 return ret;
174 }
175
imap_search_anyset_to_uidset(struct client_command_context * cmd,struct mail_search_args * args)176 void imap_search_anyset_to_uidset(struct client_command_context *cmd,
177 struct mail_search_args *args)
178 {
179 struct mail_search_arg *arg = args->args;
180
181 i_assert(arg->next == NULL);
182 switch (arg->type) {
183 case SEARCH_ALL:
184 if (arg->match_not)
185 break;
186 t_array_init(&arg->value.seqset, 1);
187 seq_range_array_add_range(&arg->value.seqset, 1, (uint32_t)-1);
188 /* fall through */
189 case SEARCH_SEQSET: {
190 ARRAY_TYPE(seq_range) seqs = arg->value.seqset;
191
192 arg->type = SEARCH_UIDSET;
193 p_array_init(&arg->value.seqset, args->pool, 16);
194 mailbox_get_uid_range(cmd->client->mailbox, &seqs,
195 &arg->value.seqset);
196 break;
197 }
198 case SEARCH_UIDSET:
199 break;
200 default:
201 i_unreached();
202 }
203 }
204
imap_search_get_searchres(struct client_command_context * cmd,struct mail_search_args ** search_args_r)205 static int imap_search_get_searchres(struct client_command_context *cmd,
206 struct mail_search_args **search_args_r)
207 {
208 struct mail_search_args *search_args;
209
210 if (client_handle_search_save_ambiguity(cmd))
211 return 0;
212
213 search_args = mail_search_build_init();
214 search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
215 if (array_is_created(&cmd->client->search_saved_uidset)) {
216 search_args->args->type = SEARCH_UIDSET;
217 p_array_init(&search_args->args->value.seqset,
218 search_args->pool,
219 array_count(&cmd->client->search_saved_uidset));
220 array_append_array(&search_args->args->value.seqset,
221 &cmd->client->search_saved_uidset);
222 } else {
223 /* $ not set yet, match nothing */
224 search_args->args->type = SEARCH_ALL;
225 search_args->args->match_not = TRUE;
226 }
227 *search_args_r = search_args;
228 return 1;
229 }
230
imap_search_get_anyset(struct client_command_context * cmd,const char * set,bool uid,struct mail_search_args ** search_args_r)231 int imap_search_get_anyset(struct client_command_context *cmd,
232 const char *set, bool uid,
233 struct mail_search_args **search_args_r)
234 {
235 const char *client_error = NULL;
236 int ret;
237
238 if (strcmp(set, "$") == 0) {
239 /* SEARCHRES extension: replace $ with the last saved
240 search result */
241 return imap_search_get_searchres(cmd, search_args_r);
242 }
243 if (!uid) {
244 ret = imap_search_get_msgset_arg(cmd, set, search_args_r,
245 &client_error);
246 } else {
247 ret = imap_search_get_uidset_arg(set, search_args_r,
248 &client_error);
249 }
250 if (ret < 0) {
251 client_send_command_error(cmd, client_error);
252 return -1;
253 }
254 return 1;
255 }
256
imap_search_add_changed_since(struct mail_search_args * search_args,uint64_t modseq)257 void imap_search_add_changed_since(struct mail_search_args *search_args,
258 uint64_t modseq)
259 {
260 struct mail_search_arg *search_arg;
261
262 search_arg = p_new(search_args->pool, struct mail_search_arg, 1);
263 search_arg->type = SEARCH_MODSEQ;
264 search_arg->value.modseq =
265 p_new(search_args->pool, struct mail_search_modseq, 1);
266 search_arg->value.modseq->modseq = modseq + 1;
267
268 search_arg->next = search_args->args->next;
269 search_args->args->next = search_arg;
270 }
271
272 struct imap_search_seqset_iter {
273 struct mail_search_args *search_args;
274 ARRAY_TYPE(seq_range) seqset_left;
275 unsigned int batch_size;
276 };
277
imap_search_seqset_next_batch(struct imap_search_seqset_iter * iter)278 static void imap_search_seqset_next_batch(struct imap_search_seqset_iter *iter)
279 {
280 array_clear(&iter->search_args->args->value.seqset);
281 seq_range_array_merge_n(&iter->search_args->args->value.seqset,
282 &iter->seqset_left, iter->batch_size);
283 }
284
285 struct imap_search_seqset_iter *
imap_search_seqset_iter_init(struct mail_search_args * search_args,uint32_t messages_count,unsigned int batch_size)286 imap_search_seqset_iter_init(struct mail_search_args *search_args,
287 uint32_t messages_count, unsigned int batch_size)
288 {
289 struct imap_search_seqset_iter *iter;
290
291 i_assert(search_args->args->next == NULL);
292
293 iter = i_new(struct imap_search_seqset_iter, 1);
294 iter->search_args = search_args;
295 iter->batch_size = batch_size;
296 mail_search_args_ref(iter->search_args);
297
298 switch (search_args->args->type) {
299 case SEARCH_SEQSET:
300 case SEARCH_UIDSET:
301 break;
302 case SEARCH_ALL:
303 if (search_args->args->match_not) {
304 /* $ used before search result was saved */
305 return iter;
306 }
307 /* 1:* - convert to seqset */
308 search_args->args->type = SEARCH_SEQSET;
309 p_array_init(&search_args->args->value.seqset,
310 search_args->pool, 1);
311 seq_range_array_add_range(&search_args->args->value.seqset,
312 1, messages_count);
313 break;
314 default:
315 i_panic("Unexpected search_args type %d",
316 search_args->args->type);
317 }
318
319 i_assert(search_args->args->type == SEARCH_SEQSET ||
320 search_args->args->type == SEARCH_UIDSET);
321
322 i_array_init(&iter->seqset_left,
323 array_count(&search_args->args->value.seqset));
324 array_append_array(&iter->seqset_left, &search_args->args->value.seqset);
325 imap_search_seqset_next_batch(iter);
326 return iter;
327 }
328
imap_search_seqset_iter_deinit(struct imap_search_seqset_iter ** _iter)329 void imap_search_seqset_iter_deinit(struct imap_search_seqset_iter **_iter)
330 {
331 struct imap_search_seqset_iter *iter = *_iter;
332
333 if (iter == NULL)
334 return;
335
336 mail_search_args_unref(&iter->search_args);
337 array_free(&iter->seqset_left);
338 i_free(iter);
339 }
340
imap_search_seqset_iter_next(struct imap_search_seqset_iter * iter)341 bool imap_search_seqset_iter_next(struct imap_search_seqset_iter *iter)
342 {
343 if (!array_is_created(&iter->seqset_left) ||
344 array_count(&iter->seqset_left) == 0)
345 return FALSE;
346
347 /* remove the last batch of searched mails from seqset_left */
348 if (seq_range_array_remove_seq_range(&iter->seqset_left,
349 &iter->search_args->args->value.seqset) == 0)
350 i_unreached();
351 imap_search_seqset_next_batch(iter);
352 return array_count(&iter->search_args->args->value.seqset) > 0;
353 }
354