1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "imap-match.h"
6 #include "mail-index.h"
7 #include "mail-storage.h"
8 #include "mail-namespace.h"
9 #include "mail-search-build.h"
10 #include "mail-search.h"
11 #include "mail-search-mime.h"
12 
13 static void
mailbox_uidset_change(struct mail_search_arg * arg,struct mailbox * box,const ARRAY_TYPE (seq_range)* search_saved_uidset)14 mailbox_uidset_change(struct mail_search_arg *arg, struct mailbox *box,
15 		      const ARRAY_TYPE(seq_range) *search_saved_uidset)
16 {
17 	struct seq_range *uids;
18 	unsigned int i, count;
19 	uint32_t seq1, seq2;
20 
21 	if (arg->value.str != NULL && strcmp(arg->value.str, "$") == 0) {
22 		/* SEARCHRES: Replace with saved uidset */
23 		array_clear(&arg->value.seqset);
24 		if (search_saved_uidset == NULL ||
25 		    !array_is_created(search_saved_uidset))
26 			return;
27 
28 		array_append_array(&arg->value.seqset, search_saved_uidset);
29 		return;
30 	}
31 
32 	arg->type = SEARCH_SEQSET;
33 
34 	/* make a copy of the UIDs */
35 	count = array_count(&arg->value.seqset);
36 	if (count == 0) {
37 		/* empty set, keep it */
38 		return;
39 	}
40 	uids = t_new(struct seq_range, count);
41 	memcpy(uids, array_front(&arg->value.seqset), sizeof(*uids) * count);
42 
43 	/* put them back to the range as sequences */
44 	array_clear(&arg->value.seqset);
45 	for (i = 0; i < count; i++) {
46 		mailbox_get_seq_range(box, uids[i].seq1, uids[i].seq2,
47 				      &seq1, &seq2);
48 		if (seq1 != 0) {
49 			seq_range_array_add_range(&arg->value.seqset,
50 						  seq1, seq2);
51 		}
52 		if (uids[i].seq2 == (uint32_t)-1) {
53 			/* make sure the last message is in the range */
54 			mailbox_get_seq_range(box, 1, (uint32_t)-1,
55 					      &seq1, &seq2);
56 			if (seq2 != 0)
57 				seq_range_array_add(&arg->value.seqset, seq2);
58 		}
59 	}
60 }
61 
62 static void
mailbox_seqset_change(struct mail_search_arg * arg,struct mailbox * box)63 mailbox_seqset_change(struct mail_search_arg *arg, struct mailbox *box)
64 {
65 	const struct seq_range *seqset;
66 	unsigned int count;
67 	uint32_t seq1, seq2;
68 
69 	seqset = array_get(&arg->value.seqset, &count);
70 	if (count > 0 && seqset[count-1].seq2 == (uint32_t)-1) {
71 		/* n:* -> n:maxseq. */
72 		mailbox_get_seq_range(box, 1, (uint32_t)-1,
73 				      &seq1, &seq2);
74 		if (seq2 == 0) {
75 			/* no mails in mailbox - nothing can match */
76 			array_clear(&arg->value.seqset);
77 		} else if (seqset[count-1].seq1 == (uint32_t)-1) {
78 			/* "*" alone needs a bit special handling
79 			   NOTE: This could be e.g. 5,* so use
80 			   seqset[last] */
81 			seq_range_array_remove(&arg->value.seqset, (uint32_t)-1);
82 			seq_range_array_add(&arg->value.seqset, seq2);
83 		} else {
84 			seq_range_array_remove_range(&arg->value.seqset,
85 						     seq2+1, (uint32_t)-1);
86 		}
87 	}
88 }
89 
90 static void
mail_search_arg_change_sets(struct mail_search_args * args,struct mail_search_arg * arg,const ARRAY_TYPE (seq_range)* search_saved_uidset)91 mail_search_arg_change_sets(struct mail_search_args *args,
92 			    struct mail_search_arg *arg,
93 			    const ARRAY_TYPE(seq_range) *search_saved_uidset)
94 {
95 	for (; arg != NULL; arg = arg->next) {
96 		switch (arg->type) {
97 		case SEARCH_SEQSET:
98 			mailbox_seqset_change(arg, args->box);
99 			break;
100 		case SEARCH_UIDSET:
101 			T_BEGIN {
102 				mailbox_uidset_change(arg, args->box,
103 						      search_saved_uidset);
104 			} T_END;
105 			break;
106 		case SEARCH_INTHREAD:
107 		case SEARCH_SUB:
108 		case SEARCH_OR:
109 			mail_search_arg_change_sets(args, arg->value.subargs,
110 						    search_saved_uidset);
111 			break;
112 		default:
113 			break;
114 		}
115 	}
116 }
117 
mail_search_arg_init(struct mail_search_args * args,struct mail_search_arg * arg)118 void mail_search_arg_init(struct mail_search_args *args,
119 			  struct mail_search_arg *arg)
120 {
121 	struct mail_search_args *thread_args;
122 	const char *keywords[2];
123 
124 	for (; arg != NULL; arg = arg->next) {
125 		switch (arg->type) {
126 		case SEARCH_MODSEQ:
127 			if (arg->value.str == NULL)
128 				break;
129 			/* fall through - modseq with keyword */
130 		case SEARCH_KEYWORDS:
131 			keywords[0] = arg->value.str;
132 			keywords[1] = NULL;
133 
134 			i_assert(arg->initialized.keywords == NULL);
135 			arg->initialized.keywords =
136 				mailbox_keywords_create_valid(args->box,
137 							      keywords);
138 			break;
139 
140 		case SEARCH_MAILBOX_GLOB: {
141 			struct mail_namespace *ns =
142 				mailbox_get_namespace(args->box);
143 
144 			arg->initialized.mailbox_glob =
145 				imap_match_init(default_pool, arg->value.str,
146 						TRUE, mail_namespace_get_sep(ns));
147 			break;
148 		}
149 		case SEARCH_INTHREAD:
150 			thread_args = arg->initialized.search_args;
151 			if (thread_args == NULL) {
152 				arg->initialized.search_args = thread_args =
153 					p_new(args->pool,
154 					      struct mail_search_args, 1);
155 				thread_args->pool = args->pool;
156 				thread_args->args = arg->value.subargs;
157 				thread_args->simplified = TRUE;
158 				thread_args->init_refcount = 1;
159 				/* simplification should have unnested all
160 				   inthreads, so we'll assume that
161 				   have_inthreads=FALSE */
162 			}
163 			thread_args->refcount++;
164 			thread_args->box = args->box;
165 			/* fall through */
166 		case SEARCH_SUB:
167 		case SEARCH_OR:
168 			mail_search_arg_init(args, arg->value.subargs);
169 			break;
170 		default:
171 			break;
172 		}
173 	}
174 }
175 
mail_search_args_init(struct mail_search_args * args,struct mailbox * box,bool change_sets,const ARRAY_TYPE (seq_range)* search_saved_uidset)176 void mail_search_args_init(struct mail_search_args *args,
177 			   struct mailbox *box, bool change_sets,
178 			   const ARRAY_TYPE(seq_range) *search_saved_uidset)
179 {
180 	i_assert(args->init_refcount <= args->refcount);
181 
182 	if (args->init_refcount++ > 0) {
183 		i_assert(args->box == box);
184 		return;
185 	}
186 
187 	args->box = box;
188 	if (change_sets) {
189 		/* Change seqsets/uidsets before simplifying the args, since it
190 		   can't handle search_saved_uidset. */
191 		mail_search_arg_change_sets(args, args->args,
192 					    search_saved_uidset);
193 	}
194 	if (!args->simplified)
195 		mail_search_args_simplify(args);
196 	mail_search_arg_init(args, args->args);
197 }
198 
mail_search_arg_deinit(struct mail_search_arg * arg)199 void mail_search_arg_deinit(struct mail_search_arg *arg)
200 {
201 	for (; arg != NULL; arg = arg->next)
202 		mail_search_arg_one_deinit(arg);
203 }
204 
mail_search_arg_one_deinit(struct mail_search_arg * arg)205 void mail_search_arg_one_deinit(struct mail_search_arg *arg)
206 {
207 	switch (arg->type) {
208 	case SEARCH_MODSEQ:
209 	case SEARCH_KEYWORDS:
210 		if (arg->initialized.keywords == NULL)
211 			break;
212 		mailbox_keywords_unref(&arg->initialized.keywords);
213 		break;
214 	case SEARCH_MAILBOX_GLOB:
215 		if (arg->initialized.mailbox_glob == NULL)
216 			break;
217 
218 		imap_match_deinit(&arg->initialized.mailbox_glob);
219 		break;
220 	case SEARCH_INTHREAD:
221 		i_assert(arg->initialized.search_args->refcount > 0);
222 		if (arg->value.search_result != NULL)
223 			mailbox_search_result_free(&arg->value.search_result);
224 		arg->initialized.search_args->refcount--;
225 		arg->initialized.search_args->box = NULL;
226 		/* fall through */
227 	case SEARCH_SUB:
228 	case SEARCH_OR:
229 		mail_search_arg_deinit(arg->value.subargs);
230 		break;
231 	default:
232 		break;
233 	}
234 }
235 
mail_search_args_deinit(struct mail_search_args * args)236 void mail_search_args_deinit(struct mail_search_args *args)
237 {
238 	if (--args->init_refcount > 0)
239 		return;
240 
241 	mail_search_arg_deinit(args->args);
242 	args->box = NULL;
243 }
244 
mail_search_args_seq2uid_sub(struct mail_search_args * args,struct mail_search_arg * arg,ARRAY_TYPE (seq_range)* uids)245 static void mail_search_args_seq2uid_sub(struct mail_search_args *args,
246 					 struct mail_search_arg *arg,
247 					 ARRAY_TYPE(seq_range) *uids)
248 {
249 	for (; arg != NULL; arg = arg->next) {
250 		switch (arg->type) {
251 		case SEARCH_SEQSET:
252 			array_clear(uids);
253 			mailbox_get_uid_range(args->box,
254 					      &arg->value.seqset, uids);
255 
256 			/* replace sequences with UIDs in the existing array.
257 			   this way it's possible to switch between uidsets and
258 			   seqsets constantly without leaking memory */
259 			arg->type = SEARCH_UIDSET;
260 			array_clear(&arg->value.seqset);
261 			array_append_array(&arg->value.seqset, uids);
262 			break;
263 		case SEARCH_SUB:
264 		case SEARCH_OR:
265 		case SEARCH_INTHREAD:
266 			mail_search_args_seq2uid_sub(args, arg->value.subargs,
267 						     uids);
268 			break;
269 		default:
270 			break;
271 		}
272 	}
273 }
274 
mail_search_args_seq2uid(struct mail_search_args * args)275 void mail_search_args_seq2uid(struct mail_search_args *args)
276 {
277 	T_BEGIN {
278 		ARRAY_TYPE(seq_range) uids;
279 
280 		t_array_init(&uids, 128);
281 		mail_search_args_seq2uid_sub(args, args->args, &uids);
282 	} T_END;
283 }
284 
mail_search_args_ref(struct mail_search_args * args)285 void mail_search_args_ref(struct mail_search_args *args)
286 {
287 	i_assert(args->refcount > 0);
288 
289 	args->refcount++;
290 }
291 
mail_search_args_unref(struct mail_search_args ** _args)292 void mail_search_args_unref(struct mail_search_args **_args)
293 {
294 	struct mail_search_args *args = *_args;
295 
296 	i_assert(args->refcount > 0);
297 
298 	*_args = NULL;
299 	if (--args->refcount > 0) {
300 		i_assert(args->init_refcount <= args->refcount);
301 		return;
302 	}
303 	i_assert(args->init_refcount <= 1);
304 	if (args->init_refcount == 1)
305 		mail_search_args_deinit(args);
306 	pool_unref(&args->pool);
307 }
308 
309 static struct mail_search_arg *
mail_search_arg_dup_one(pool_t pool,const struct mail_search_arg * arg)310 mail_search_arg_dup_one(pool_t pool, const struct mail_search_arg *arg)
311 {
312 	struct mail_search_arg *new_arg;
313 
314 	new_arg = p_new(pool, struct mail_search_arg, 1);
315 	new_arg->type = arg->type;
316 	new_arg->match_not = arg->match_not;
317 	new_arg->match_always = arg->match_always;
318 	new_arg->nonmatch_always = arg->nonmatch_always;
319 	new_arg->fuzzy = arg->fuzzy;
320 	new_arg->value.search_flags = arg->value.search_flags;
321 
322 	switch (arg->type) {
323 	case SEARCH_INTHREAD:
324 		new_arg->value.thread_type = arg->value.thread_type;
325 		/* fall through */
326 	case SEARCH_OR:
327 	case SEARCH_SUB:
328 		new_arg->value.subargs =
329 			mail_search_arg_dup(pool, arg->value.subargs);
330 		break;
331 	case SEARCH_ALL:
332 	case SEARCH_SAVEDATESUPPORTED:
333 		break;
334 	case SEARCH_SEQSET:
335 	case SEARCH_UIDSET:
336 	case SEARCH_REAL_UID:
337 		p_array_init(&new_arg->value.seqset, pool,
338 			     array_count(&arg->value.seqset));
339 		array_append_array(&new_arg->value.seqset, &arg->value.seqset);
340 		break;
341 	case SEARCH_FLAGS:
342 		new_arg->value.flags = arg->value.flags;
343 		break;
344 	case SEARCH_BEFORE:
345 	case SEARCH_ON:
346 	case SEARCH_SINCE:
347 		new_arg->value.time = arg->value.time;
348 		new_arg->value.date_type = arg->value.date_type;
349 		break;
350 	case SEARCH_SMALLER:
351 	case SEARCH_LARGER:
352 		new_arg->value.size = arg->value.size;
353 		break;
354 	case SEARCH_HEADER:
355 	case SEARCH_HEADER_ADDRESS:
356 	case SEARCH_HEADER_COMPRESS_LWSP:
357 		new_arg->hdr_field_name = p_strdup(pool, arg->hdr_field_name);
358 		/* fall through */
359 	case SEARCH_KEYWORDS:
360 	case SEARCH_BODY:
361 	case SEARCH_TEXT:
362 	case SEARCH_GUID:
363 	case SEARCH_MAILBOX:
364 	case SEARCH_MAILBOX_GUID:
365 	case SEARCH_MAILBOX_GLOB:
366 		new_arg->value.str = p_strdup(pool, arg->value.str);
367 		break;
368 	case SEARCH_MODSEQ:
369 		new_arg->value.modseq =
370 			p_new(pool, struct mail_search_modseq, 1);
371 		*new_arg->value.modseq = *arg->value.modseq;
372 		break;
373 	case SEARCH_MIMEPART:
374 		new_arg->value.mime_part =
375 			mail_search_mime_part_dup(pool, arg->value.mime_part);
376 		break;
377 	}
378 	return new_arg;
379 }
380 
381 struct mail_search_arg *
mail_search_arg_dup(pool_t pool,const struct mail_search_arg * arg)382 mail_search_arg_dup(pool_t pool, const struct mail_search_arg *arg)
383 {
384 	struct mail_search_arg *new_arg = NULL, **dest = &new_arg;
385 
386 	for (; arg != NULL; arg = arg->next) {
387 		*dest = mail_search_arg_dup_one(pool, arg);
388 		dest = &(*dest)->next;
389 	}
390 	return new_arg;
391 }
392 
393 struct mail_search_args *
mail_search_args_dup(const struct mail_search_args * args)394 mail_search_args_dup(const struct mail_search_args *args)
395 {
396 	struct mail_search_args *new_args;
397 
398 	new_args = mail_search_build_init();
399 	new_args->simplified = args->simplified;
400 	new_args->have_inthreads = args->have_inthreads;
401 	new_args->args = mail_search_arg_dup(new_args->pool, args->args);
402 	return new_args;
403 }
404 
mail_search_args_reset(struct mail_search_arg * args,bool full_reset)405 void mail_search_args_reset(struct mail_search_arg *args, bool full_reset)
406 {
407 	while (args != NULL) {
408 		if (args->type == SEARCH_OR || args->type == SEARCH_SUB)
409 			mail_search_args_reset(args->value.subargs, full_reset);
410 
411 		if (args->match_always) {
412 			if (!full_reset)
413 				args->result = 1;
414 			else {
415 				args->match_always = FALSE;
416 				args->result = -1;
417 			}
418 		} else if (args->nonmatch_always) {
419 			if (!full_reset)
420 				args->result = 0;
421 			else {
422 				args->nonmatch_always = FALSE;
423 				args->result = -1;
424 			}
425 		} else {
426 			args->result = -1;
427 		}
428 
429 		args = args->next;
430 	}
431 }
432 
search_arg_foreach(struct mail_search_arg * arg,mail_search_foreach_callback_t * callback,void * context)433 static void search_arg_foreach(struct mail_search_arg *arg,
434 			       mail_search_foreach_callback_t *callback,
435 			       void *context)
436 {
437 	struct mail_search_arg *subarg;
438 
439 	if (arg->result != -1)
440 		return;
441 
442 	if (arg->type == SEARCH_SUB) {
443 		/* sublist of conditions */
444 		i_assert(arg->value.subargs != NULL);
445 
446 		arg->result = 1;
447 		subarg = arg->value.subargs;
448 		while (subarg != NULL) {
449 			if (subarg->result == -1)
450 				search_arg_foreach(subarg, callback, context);
451 
452 			if (subarg->result == -1)
453 				arg->result = -1;
454 			else if (subarg->result == 0) {
455 				/* didn't match */
456 				arg->result = 0;
457 				break;
458 			}
459 
460 			subarg = subarg->next;
461 		}
462 		if (arg->match_not && arg->result != -1)
463 			arg->result = arg->result > 0 ? 0 : 1;
464 	} else if (arg->type == SEARCH_OR) {
465 		/* OR-list of conditions */
466 		i_assert(arg->value.subargs != NULL);
467 
468 		subarg = arg->value.subargs;
469 		arg->result = 0;
470 		while (subarg != NULL) {
471 			if (subarg->result == -1)
472 				search_arg_foreach(subarg, callback, context);
473 
474 			if (subarg->result == -1)
475 				arg->result = -1;
476 			else if (subarg->result > 0) {
477 				/* matched */
478 				arg->result = 1;
479 				break;
480 			}
481 
482 			subarg = subarg->next;
483 		}
484 		if (arg->match_not && arg->result != -1)
485 			arg->result = arg->result > 0 ? 0 : 1;
486 	} else {
487 		/* just a single condition */
488 		callback(arg, context);
489 	}
490 }
491 
492 #undef mail_search_args_foreach
mail_search_args_foreach(struct mail_search_arg * args,mail_search_foreach_callback_t * callback,void * context)493 int mail_search_args_foreach(struct mail_search_arg *args,
494 			     mail_search_foreach_callback_t *callback,
495 			     void *context)
496 {
497 	int result;
498 
499 	result = 1;
500 	for (; args != NULL; args = args->next) {
501 		search_arg_foreach(args, callback, context);
502 
503 		if (args->result == 0) {
504 			/* didn't match */
505 			return 0;
506 		}
507 
508 		if (args->result == -1)
509 			result = -1;
510 	}
511 
512 	return result;
513 }
514 
515 static void
search_arg_analyze(struct mail_search_arg * arg,buffer_t * headers,bool * have_body,bool * have_text)516 search_arg_analyze(struct mail_search_arg *arg, buffer_t *headers,
517 		   bool *have_body, bool *have_text)
518 {
519 	static const char *date_hdr = "Date";
520 	struct mail_search_arg *subarg;
521 
522 	if (arg->result != -1)
523 		return;
524 
525 	switch (arg->type) {
526 	case SEARCH_OR:
527 	case SEARCH_SUB:
528 		subarg = arg->value.subargs;
529 		while (subarg != NULL) {
530 			if (subarg->result == -1) {
531 				search_arg_analyze(subarg, headers,
532 						   have_body, have_text);
533 			}
534 
535 			subarg = subarg->next;
536 		}
537 		break;
538 	case SEARCH_BEFORE:
539 	case SEARCH_ON:
540 	case SEARCH_SINCE:
541 		if (arg->value.date_type == MAIL_SEARCH_DATE_TYPE_SENT)
542 			buffer_append(headers, &date_hdr, sizeof(const char *));
543 		break;
544 	case SEARCH_HEADER:
545 	case SEARCH_HEADER_ADDRESS:
546 	case SEARCH_HEADER_COMPRESS_LWSP:
547 		buffer_append(headers, &arg->hdr_field_name,
548 			      sizeof(const char *));
549 		break;
550 	case SEARCH_BODY:
551 		*have_body = TRUE;
552 		break;
553 	case SEARCH_TEXT:
554 		*have_text = TRUE;
555 		*have_body = TRUE;
556 		break;
557 	default:
558 		break;
559 	}
560 }
561 
562 const char *const *
mail_search_args_analyze(struct mail_search_arg * args,bool * have_headers,bool * have_body)563 mail_search_args_analyze(struct mail_search_arg *args,
564 			 bool *have_headers, bool *have_body)
565 {
566 	const char *null = NULL;
567 	buffer_t *headers;
568 	bool have_text;
569 
570 	*have_headers = *have_body = have_text = FALSE;
571 
572 	headers = t_buffer_create(128);
573 	for (; args != NULL; args = args->next)
574 		search_arg_analyze(args, headers, have_body, &have_text);
575 
576 	*have_headers = have_text || headers->used != 0;
577 
578 	if (headers->used == 0)
579 		return NULL;
580 
581 	buffer_append(headers, &null, sizeof(const char *));
582 	return headers->data;
583 }
584 
585 static bool
mail_search_args_match_mailbox_arg(const struct mail_search_arg * arg,const char * vname,char sep)586 mail_search_args_match_mailbox_arg(const struct mail_search_arg *arg,
587 				   const char *vname, char sep)
588 {
589 	const struct mail_search_arg *subarg;
590 	bool ret;
591 
592 	switch (arg->type) {
593 	case SEARCH_OR:
594 		subarg = arg->value.subargs;
595 		for (; subarg != NULL; subarg = subarg->next) {
596 			if (mail_search_args_match_mailbox_arg(subarg,
597 							       vname, sep))
598 				return TRUE;
599 		}
600 		return FALSE;
601 	case SEARCH_SUB:
602 	case SEARCH_INTHREAD:
603 		subarg = arg->value.subargs;
604 		for (; subarg != NULL; subarg = subarg->next) {
605 			if (!mail_search_args_match_mailbox_arg(subarg,
606 								vname, sep))
607 				return FALSE;
608 		}
609 		return TRUE;
610 	case SEARCH_MAILBOX:
611 		ret = strcmp(arg->value.str, vname) == 0;
612 		return ret != arg->match_not;
613 	case SEARCH_MAILBOX_GLOB: {
614 		T_BEGIN {
615 			struct imap_match_glob *glob;
616 
617 			glob = imap_match_init(pool_datastack_create(),
618 					       arg->value.str, TRUE, sep);
619 			ret = imap_match(glob, vname) == IMAP_MATCH_YES;
620 		} T_END;
621 		return ret != arg->match_not;
622 	}
623 	default:
624 		break;
625 	}
626 	return TRUE;
627 }
628 
mail_search_args_match_mailbox(struct mail_search_args * args,const char * vname,char sep)629 bool mail_search_args_match_mailbox(struct mail_search_args *args,
630 				    const char *vname, char sep)
631 {
632 	const struct mail_search_arg *arg;
633 
634 	if (!args->simplified)
635 		mail_search_args_simplify(args);
636 
637 	for (arg = args->args; arg != NULL; arg = arg->next) {
638 		if (!mail_search_args_match_mailbox_arg(arg, vname, sep))
639 			return FALSE;
640 	}
641 	return TRUE;
642 }
643 
mail_search_arg_one_equals(const struct mail_search_arg * arg1,const struct mail_search_arg * arg2)644 bool mail_search_arg_one_equals(const struct mail_search_arg *arg1,
645 				const struct mail_search_arg *arg2)
646 {
647 	if (arg1->type != arg2->type ||
648 	    arg1->match_not != arg2->match_not ||
649 	    arg1->fuzzy != arg2->fuzzy ||
650 	    arg1->value.search_flags != arg2->value.search_flags)
651 		return FALSE;
652 
653 	switch (arg1->type) {
654 	case SEARCH_OR:
655 	case SEARCH_SUB:
656 		return mail_search_arg_equals(arg1->value.subargs,
657 					      arg2->value.subargs);
658 
659 	case SEARCH_ALL:
660 	case SEARCH_SAVEDATESUPPORTED:
661 		return TRUE;
662 	case SEARCH_SEQSET:
663 		/* sequences may point to different messages at different times,
664 		   never assume they match */
665 		return FALSE;
666 	case SEARCH_UIDSET:
667 		return array_cmp(&arg1->value.seqset, &arg2->value.seqset);
668 	case SEARCH_REAL_UID:
669 		return array_cmp(&arg1->value.seqset, &arg2->value.seqset);
670 
671 	case SEARCH_FLAGS:
672 		return arg1->value.flags == arg2->value.flags;
673 	case SEARCH_KEYWORDS:
674 		return strcasecmp(arg1->value.str, arg2->value.str) == 0;
675 
676 	case SEARCH_BEFORE:
677 	case SEARCH_ON:
678 	case SEARCH_SINCE:
679 		return arg1->value.time == arg2->value.time &&
680 			arg1->value.date_type == arg2->value.date_type;
681 
682 	case SEARCH_SMALLER:
683 	case SEARCH_LARGER:
684 		return arg1->value.size == arg2->value.size;
685 
686 	case SEARCH_HEADER:
687 	case SEARCH_HEADER_ADDRESS:
688 	case SEARCH_HEADER_COMPRESS_LWSP:
689 		if (strcasecmp(arg1->hdr_field_name, arg2->hdr_field_name) != 0)
690 			return FALSE;
691 		/* fall through */
692 	case SEARCH_BODY:
693 	case SEARCH_TEXT:
694 	case SEARCH_GUID:
695 	case SEARCH_MAILBOX:
696 	case SEARCH_MAILBOX_GUID:
697 	case SEARCH_MAILBOX_GLOB:
698 		/* don't bother doing case-insensitive comparison. it must not
699 		   be done for guid/mailbox, and for others we should support
700 		   full i18n case-insensitivity (or the active comparator
701 		   in future). */
702 		return strcmp(arg1->value.str, arg2->value.str) == 0;
703 
704 	case SEARCH_MODSEQ: {
705 		const struct mail_search_modseq *m1 = arg1->value.modseq;
706 		const struct mail_search_modseq *m2 = arg2->value.modseq;
707 
708 		return m1->modseq == m2->modseq &&
709 			m1->type == m2->type;
710 	}
711 	case SEARCH_INTHREAD:
712 		if (arg1->value.thread_type != arg2->value.thread_type)
713 			return FALSE;
714 		return mail_search_arg_equals(arg1->value.subargs,
715 					      arg2->value.subargs);
716 	case SEARCH_MIMEPART:
717 		return mail_search_mime_parts_equal(arg1->value.mime_part,
718 					      arg2->value.mime_part);
719 
720 	}
721 	i_unreached();
722 }
723 
mail_search_arg_equals(const struct mail_search_arg * arg1,const struct mail_search_arg * arg2)724 bool mail_search_arg_equals(const struct mail_search_arg *arg1,
725 			    const struct mail_search_arg *arg2)
726 {
727 	while (arg1 != NULL && arg2 != NULL) {
728 		if (!mail_search_arg_one_equals(arg1, arg2))
729 			return FALSE;
730 		arg1 = arg1->next;
731 		arg2 = arg2->next;
732 	}
733 	return arg1 == NULL && arg2 == NULL;
734 }
735 
mail_search_args_equal(const struct mail_search_args * args1,const struct mail_search_args * args2)736 bool mail_search_args_equal(const struct mail_search_args *args1,
737 			    const struct mail_search_args *args2)
738 {
739 	i_assert(args1->simplified == args2->simplified);
740 	i_assert(args1->box == args2->box);
741 
742 	return mail_search_arg_equals(args1->args, args2->args);
743 }
744 
745 static void
mail_search_args_result_serialize_arg(const struct mail_search_arg * arg,buffer_t * dest)746 mail_search_args_result_serialize_arg(const struct mail_search_arg *arg,
747 				      buffer_t *dest)
748 {
749 	const struct mail_search_arg *subarg;
750 
751 	buffer_append_c(dest, arg->result < 0 ? 0xff : arg->result);
752 
753 	switch (arg->type) {
754 	case SEARCH_OR:
755 	case SEARCH_SUB:
756 	case SEARCH_INTHREAD:
757 		subarg = arg->value.subargs;
758 		for (; subarg != NULL; subarg = subarg->next)
759 			mail_search_args_result_serialize_arg(subarg, dest);
760 	default:
761 		break;
762 	}
763 }
764 
mail_search_args_result_serialize(const struct mail_search_args * args,buffer_t * dest)765 void mail_search_args_result_serialize(const struct mail_search_args *args,
766 				       buffer_t *dest)
767 {
768 	const struct mail_search_arg *arg;
769 
770 	for (arg = args->args; arg != NULL; arg = arg->next)
771 		mail_search_args_result_serialize_arg(arg, dest);
772 }
773 
774 static void
mail_search_args_result_deserialize_arg(struct mail_search_arg * arg,const unsigned char ** data,size_t * size)775 mail_search_args_result_deserialize_arg(struct mail_search_arg *arg,
776 					const unsigned char **data,
777 					size_t *size)
778 {
779 	struct mail_search_arg *subarg;
780 
781 	i_assert(*size > 0);
782 	arg->result = **data == 0xff ? -1 : **data;
783 	*data += 1; *size -= 1;
784 
785 	switch (arg->type) {
786 	case SEARCH_OR:
787 	case SEARCH_SUB:
788 	case SEARCH_INTHREAD:
789 		subarg = arg->value.subargs;
790 		for (; subarg != NULL; subarg = subarg->next) {
791 			mail_search_args_result_deserialize_arg(subarg,
792 								data, size);
793 		}
794 	default:
795 		break;
796 	}
797 }
798 
mail_search_args_result_deserialize(struct mail_search_args * args,const unsigned char * data,size_t size)799 void mail_search_args_result_deserialize(struct mail_search_args *args,
800 					 const unsigned char *data, size_t size)
801 {
802 	struct mail_search_arg *arg;
803 
804 	for (arg = args->args; arg != NULL; arg = arg->next)
805 		mail_search_args_result_deserialize_arg(arg, &data, &size);
806 }
807