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