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