1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "imap-common.h"
4 #include "mail-storage.h"
5 #include "mail-search-build.h"
6 #include "imap-search-args.h"
7 #include "imap-expunge.h"
8 
9 #define IMAP_EXPUNGE_BATCH_SIZE 1000
10 
11 /* get a seqset of all the mails with \Deleted */
imap_expunge_get_seqset(struct mailbox * box,struct mail_search_arg * next_search_arg,ARRAY_TYPE (seq_range)* seqset)12 static int imap_expunge_get_seqset(struct mailbox *box,
13 				   struct mail_search_arg *next_search_arg,
14 				   ARRAY_TYPE(seq_range) *seqset)
15 {
16 	struct mailbox_transaction_context *t;
17 	struct mail_search_args *search_args;
18 	struct mail_search_context *ctx;
19 	struct mail *mail;
20 	int ret;
21 
22 	search_args = mail_search_build_init();
23 	search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
24 	search_args->args->type = SEARCH_FLAGS;
25 	search_args->args->value.flags = MAIL_DELETED;
26 	search_args->args->next = next_search_arg;
27 
28 	/* Refresh the flags so we'll expunge all messages marked as \Deleted
29 	   by any session. */
30 	t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_REFRESH,
31 				      "EXPUNGE");
32 	ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
33 
34 	/* collect the seqs into a seqset */
35 	while (mailbox_search_next(ctx, &mail))
36 		seq_range_array_add(seqset, mail->seq);
37 
38 	ret = mailbox_search_deinit(&ctx);
39 	/* commit in case a plugin made changes - failures should not abort the expunge */
40 	(void) mailbox_transaction_commit(&t);
41 	mail_search_args_unref(&search_args);
42 
43 	if (ret < 0)
44 		array_free(seqset);
45 
46 	return ret;
47 }
48 
imap_expunge(struct mailbox * box,struct mail_search_arg * next_search_arg,unsigned int * expunged_count)49 int imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg,
50 		 unsigned int *expunged_count)
51 {
52 	struct imap_search_seqset_iter *seqset_iter;
53 	struct mail_search_args *search_args;
54 	struct mailbox_status status;
55 	bool expunges = FALSE;
56 	int ret;
57 
58 	if (mailbox_is_readonly(box)) {
59 		/* silently ignore */
60 		return 0;
61 	}
62 
63 	mailbox_get_open_status(box, STATUS_MESSAGES, &status);
64 
65 	search_args = mail_search_build_init();
66 	search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
67 	search_args->args->type = SEARCH_SEQSET;
68 	p_array_init(&search_args->args->value.seqset, search_args->pool, 16);
69 
70 	if (imap_expunge_get_seqset(box, next_search_arg,
71 				    &search_args->args->value.seqset) < 0) {
72 		mail_search_args_unref(&search_args);
73 		return -1;
74 	}
75 
76 	seqset_iter = imap_search_seqset_iter_init(search_args, status.messages,
77 						   IMAP_EXPUNGE_BATCH_SIZE);
78 
79 	do {
80 		struct mailbox_transaction_context *t;
81 		struct mail_search_context *ctx;
82 		struct mail *mail;
83 
84 		t = mailbox_transaction_begin(box, 0, "EXPUNGE");
85 		ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
86 
87 		while (mailbox_search_next(ctx, &mail)) {
88 			*expunged_count += 1;
89 			mail_expunge(mail);
90 			expunges = TRUE;
91 		}
92 
93 		ret = mailbox_search_deinit(&ctx);
94 		if (ret < 0) {
95 			mailbox_transaction_rollback(&t);
96 			break;
97 		} else {
98 			ret = mailbox_transaction_commit(&t);
99 			if (ret < 0)
100 				break;
101 		}
102 	} while (imap_search_seqset_iter_next(seqset_iter));
103 
104 	imap_search_seqset_iter_deinit(&seqset_iter);
105 	mail_search_args_unref(&search_args);
106 
107 	if (ret < 0)
108 		return ret;
109 
110 	return expunges ? 1 : 0;
111 }
112